经过整整一年的时间,今天终于实现了梦寐以求的控制空调的程序,回顾这段历程,大有曲折轮回之意。
去年此时我从xx辞职了,那时候我留下了一个空调的问题而悬而未决,但是其实走的时候我已经说过了,要用单片机捕捉原始码,不要解码,因为当时我做的电视遥控学习是用的网上的一段程序做的,是解码的,始终没有做这个空调的学习原始码和控制。后来听说后来者做出来了,也就印证了我的思想没有错。然后这个问题在我这里就被搁置了!挂起了。
一方面是我的主要工作不在是智能家居,两一方面是程序上原来的固件库有问题。不太好做。直到上周吧,近来xx等人做的空调和电视的控制深深的刺激了我,我纵然是不服输的,和他交流了几次,总是骄纵的样子,仿佛他做出来就没有别人一样。于是乎,暗下决心,一定要攻破此问题,他能做我们就不能做?总得试试才知道,说干就干,重新找出来去年的代码,都看不懂了,然后开始分步走。
第一步:确定好TICK时间在基准,
第二步:模拟采样PWM波形,
第三步:采集红外数据
第四步:PWM调制红外波形发送
主要粗分为以上4步。然后分而治之。第一步就遇到很大问题,也是老问题,就是固件库怎么也不能把时钟提高到我需要的精度,并且老有迟滞误差的出现。为了解决这个问题。我专门去官方网站下载了最新的寄存器库,XX人总是自信的认为他是操作寄存器起家的,殊不知大家都是那样过来的,忽悠纯软件的人可以,忽悠我你还是省省得好。然后对比两套库的差异,很明显出来了,是由于代码的回调时的进出栈带来的迟滞,虽然进出、栈时间很短,CPU速度足够快,但是由于是精度比较高,所以速度很快,多次的堆积压栈出栈就会导致时间被占用,于是我在老版本的程序上稍加改动,屏蔽掉系统回调,速度直接呼呼上去。于是这个问题解决了,此问题已解决问题就已经解决一半了,
今天实现了剩下的3步,上午主要工作是捕捉,很顺利,捕捉很成功,这也是磨刀不误砍柴工的结果。然后自己写了一点逻辑算法,用来接收和发送之间的协同。下午实现数据码的重构,利用PWM发生的38K重构整个码流,下午为了这个特意去办公室拿了示波器。架起来示波器直到发出来的和接收到遥控的数据码一个不差的显示到屏幕上,我知道大概我要去办公室实际测试一下了。
到了办公室废了好一顿周折才进去,没带钥匙,无奈只能爬进去了。一切就绪后,学码,发码。空调哗唥哗唥的跟着变化,我知道成功了,呵呵!
代码如下:
代码简单的不能再简单,几行而已。只是XXX别再装逼,都能!原理都一样只是你做的稍微细一点,我的这个粒度足够我用。
uint8_t StartLeve=0; //起始电平状态
uint16_t DataLenth=0; //码型长度
uint16_t Learn_Finsh=0; //学习完毕
uint16_t ir_raw[512]; //数据码
uint32_t ir_number; //计数器
/*
描述:发送红外调制嘛
************************/
extern void DrvTIMER_Delay(E_TIMER_CHANNEL ch, uint32_t uIntTicks) ;
void Send_Ir_Code(uint8_t *pt ,uint16_t Lenth )
{
uint16_t i;
uint8_t k=0;
uint32_t da,da1;
for(i=1;i<Lenth+1;i+=2)
{
DrvPWM_Enable(DRVPWM_TIMER0, 1);
da = ir_raw;
DrvTIMER_Delay( E_TMR0, da) ;
DrvPWM_Enable(DRVPWM_TIMER0, 0);
da1 = ir_raw[i+1];
DrvTIMER_Delay( E_TMR0, da1) ;
}
DrvPWM_Enable(DRVPWM_TIMER0, 0);
}
void EINT0callback()
{
ir_raw[DataLenth]=ir_number;//更新计数器
ir_number=0; //清计数器
if(DataLenth==0) //获取起始电平,以后按照数组依次排列为高低高低.
{
if(DrvGPIO_GetBit(E_PORT3,E_PIN2)==0)
{
StartLeve = 0;
}
else //取得起始电平
{
StartLeve = 1;
}
}
//100MS 作为数据码结束的标志,理论上没有大于100MS的红外间隔,可改变。
//但是要注意再学习的时候学完一次之后得在按一下,触发之。
//此时已经学到了数据并可以返回数据的长度啦。
if((ir_raw[DataLenth]>10000)&&(DataLenth!=0))
{
//学码完成 大于0之后表示数据已经记录进缓存中了
// Learn_Finsh = DataLenth-1
//RAW数据被记录在raw[1]-raw[Learn_Finsh]之间
//
Learn_Finsh= DataLenth-1;//
DataLenth=0; //准备下一次学习。
}
DataLenth++; //下一个数据码
}
void pfP2P3P4Callback(uint32_t u32P2Status, uint32_t u32P3Status, uint32_t u32P4Status)
{
}
void pfP0P1Callback (uint32_t u32P0Status, uint32_t u32P1Status)
{
if(u32P0Status&(1<<0)) //p0.0
{
}
}
void init_IR_INTTRUPUT(void)
{
//DrvGPIO_SetIntCallback(pfP0P1Callback, pfP2P3P4Callback);//install
//DrvGPIO_EnableInt(E_PORT0, E_PIN0, E_IO_BOTH_EDGE, E_MODE_EDGE);//双边沿
DrvGPIO_EnableEINT(E_EINT0_PIN,E_IO_BOTH_EDGE, E_MODE_EDGE, EINT0callback);
}
void init_PWM(void)
{
S_DRVPWM_TIME_DATA_T sP_PWM;
/*Initialize PWM*/
DrvPWM_Open();
/* Select PWM engine clock */
DrvPWM_SelectClockSource(DRVPWM_TIMER0, DRVPWM_INTERNAL_22M);
/* PWM Timer property */
sP_PWM.u8Mode = DRVPWM_AUTO_RELOAD_MODE;
sP_PWM.u8HighPulseRatio = 50; /* High Pulse peroid : Total Pulse peroid = 1 : 100 */
sP_PWM.i32Inverter = 1;
sP_PWM.u32Frequency =38000;
DrvPWM_SetTimerClk(DRVPWM_TIMER0, &sP_PWM);
/* Enable Output for PWM Timer */
DrvPWM_SetTimerIO(DRVPWM_TIMER0, 1);
/* Enable the PWM Timer */
// DrvPWM_Enable(DRVPWM_TIMER0, 0);
DrvGPIO_InitFunction(E_FUNC_PWM01_P4);
DrvPWM_Enable(DRVPWM_TIMER0, 0);
}
到最后做出来了,单独使用确实没啥意思,如果联合TCP/IP使用那就有意义的多了。数据码放在本地FLASH中,服务器只要发送代号即可,小米盒子的遥控功能就是这么做的!
最后结句以自欺:
结庐在人境,而无车马喧。 问君何能尔?心远地自偏。 采菊东篱下,悠然见南山。 山气日夕佳,飞鸟相与还。 此中有真意,欲辨已忘言。
老伟
于日照比特电子
|