机智云GoKitwilliam hill官网
直播中

韦声健

12年用户 439经验值
擅长:可编程逻辑 嵌入式技术 接口/总线/驱动 控制/MCU
私信 关注
[GoKit作品]

【GoKit试用体验】智能多彩云灯之红外接收模块

出差一周,刚回来又陪家人过端午,今天有空,继续上期的工作,首先完成多彩云灯的红外接收模块,因为我计划第一步实现红外遥控器控制多彩云灯,第二步实现手机端控制,不能一口吃成胖子
红外接收思路
了解一下红外信号的基本构成,看图
1.png
图片中就是一个常见红外遥控发出的信号,1和0的区别在于低电平持续的时间不同,一个正常的遥控指令包含起始码,2字节地址正、反码,2字节操作正、反码,停止位
实现接收的思路就是将一个红外接收头连接到IO口上,在GoKit上我将它连接在A0口,实际就是PA0,然后使用外部中断,一旦接收到上升沿就开始进入外部中断,然后统计两个上升沿之间的时间,通过判断时间的长短确定是起始码还是0或1,完成32个bit接收时判断正反码是否匹配,如果匹配,那么就接收到一个正确的遥控按键值。
使用微信宠物那个工程,官网可以下载。尽量不修改原来的代码,因为以后还要做手机端开,增加红外接收部分即可。
从以上思路,需要完成的工作包括一下内容:
1、硬件连接,这个就是把红外接收头焊接或其他方式连接到板上A0口,实现5V供电。
2、配置IO口,实现外部中断配置,我使用的外部中断0。
3、实现中断服务程序。
4、使用Systick作为红外接收的脉冲计时器。

修改延时代码
原来的工程代码还是做了一些修改,主要是延时部分,因为GoKit使用了SysTick作延时控制,实现方式不是我想要的。因为我也想使用SysTick来计时,因此我先修改原来的代码,来看一下原来的部分代码
void Delay_us(uint32_t nus)
{               
                uint32_t temp;  
                SysTick->LOAD = nus*fac_us;  
                SysTick->VAL=0x00;
                SysTick->CTRL = 0x01;
                do  
                {  
                         temp = SysTick->CTRL;   
                }  
                while(temp & 0x01 && !(temp &(1<<16)));        
                SysTick->CTRL = 0x00;  
                SysTick->VAL = 0x00;                                                                                                                 
}

这个是延时us的原代码,中文注释拷贝不出来。
SysTick是一个24位的递减计时器,到0的时候会产生异常,并重装载。
源代码将其时钟配置为1M,每减1表示1us,以上代码可以看出,调用这个函数,设置一个值,当减到0的时候表示完成延时,在原来的系统中没有问题,但是中断需要计时的情况就会产生冲突,这个函数的清除操作会破坏中断的计时。
为此,我作了一些修改,使得原来的代码调用这个函数的时候,时间大致不会有问题,又能与中断共用计时器。
来看一下代码:
void Delay_Init(uint8_t SYSCLK)
{
        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
        fac_us=SYSCLK/8;
        fac_ms =(u16)fac_us*1000;        
        SysTick->LOAD = 0x00FFFFFF;
        SysTick->VAL=0x00;
        SysTick->CTRL = 0x01;
}       
初始化部分将重装载值设为最大的24位为1,一旦减到0就开始重头来过,默认初始化完成就启动SysTick,速率还是1M不变,每减1表示1us。
                                                                                      
void Delay_us(uint32_t nus)
{               
        uint32_t temp;
        uint32_t delay_time;
        uint32_t time1;
        uint32_t time2;

        delay_time = nus*fac_us;
        time1 = SysTick->VAL;
       
        do  
        {  
                time2 = SysTick->VAL;
                if( time1 >= time2 )
                {
                        temp = time1 - time2;
                }
                else
                {
                        temp = 0x00FFFFFF - time2 + time1;
                }
        }  
        while( temp < delay_time );       
}
us的延时函数改了,调用函数的时候记录一个时刻,运行时不停读取另一个时刻,时间达到延时要求的时候就退出,全程不会修改SysTick的寄存器,如果这时候中断也使用这样的计时方式,那么相互不会影响。

void Delay_ms(uint16_t nms)
{                                     
        Delay_us(1000 * nms);       
}

延时1ms我就简单粗暴的直接调用上面的us函数,延时乘以1000就是ms了。

简单测试一下,这样改原来的系统运行没有问题。


接收实现代码
数据初始化部分,将红外接收的标志等数据清零。
void IR_RemoteInit(void)
{
        IR_Remote.ID       = IR_RemoteID
        IR_Remote.KeyVal   = IR_DataNull;  
        IR_Remote.RepetCnt = 0;            
       
        IR_Remote.RxDoneFlag    = FALSE;   
        IR_Remote.RxStartFlag   = FALSE;   
        IR_Remote.HeadFlag      = FALSE;
       
        IR_Remote.BitCnt        = 0;
        IR_Remote.RxBit         = 0;               

        IR_Remote.LastTime    = 0;      
        IR_Remote.CurrentTime = 0;   
        IR_Remote.PulseTime   = 0;   
        IR_Remote.RxTimeOut   = 0;
}

硬件初始化,将红外接收使用PA0口配置为外部中断,值得注意的是,红外接收头没有上拉,也就是传说中的开漏输出,所以一定要将信号的IO口配置为内部上拉,注意中断优先级不要与原来系统使用的冲突了。
void IR_Remote_GPIOInit(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;

        IR_RemoteInit();
       
    RCC_APB2PeriphClockCmd(GPIO_IR_Remote_CLK, ENABLE);

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    NVIC_InitStructure.NVIC_IRQChannel = IR_Remote_EXTI_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin   = GPIO_IR_Remote_PIN;
    GPIO_Init(GPIO_IR_Remote_PORT, &GPIO_InitStructure);
    GPIO_EXTILineConfig(GPIO_IR_Remote_PortSource, GPIO_IR_Remote_PinSource);  

    EXTI_InitStructure.EXTI_Line    = IR_Remote_EXTI_Line;
    EXTI_InitStructure.EXTI_Mode    = IR_Remote_EXTI_Mode;
    EXTI_InitStructure.EXTI_Trigger = IR_Remote_EXTI_Trigger;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
       
        printf("IR remote Init OKrn");
}



中断服务程序,第一次进中断需要特别处理,首先接收起始码,时间关系与前面的图不一样,因为我这遥控器不知道是什么厂家的,没有资料,我通过调试确定了起始码,0和1大致的时间范围,然后识别出所有的按键,正反码都是正确匹配的。调试的时候可以加一些打印信息,正式的就不要再中断里面打印了,太费时间。
void IR_DecodeISR(void)
{

        if(GPIO_ReadInputDataBit( GPIO_IR_Remote_PORT, GPIO_IR_Remote_PIN ) )  
        {
                if( IR_Remote.RxStartFlag == FALSE )
                {
                        IR_Remote.LastTime    = SysTick->VAL;
                        IR_Remote.CurrentTime = SysTick->VAL;
                        IR_Remote.RxStartFlag = TRUE;
                        IR_Remote.RxTimeOut   = IR_TIME_OUT_VAL_MS;
                        return;
                }

                IR_Remote.LastTime = IR_Remote.CurrentTime;
                IR_Remote.CurrentTime = SysTick->VAL;     
               
                if(IR_Remote.CurrentTime <=  IR_Remote.LastTime)
                {
                        IR_Remote.PulseTime = IR_Remote.LastTime - IR_Remote.CurrentTime;
                }
                else
                {
                        IR_Remote.PulseTime = 0x00FFFFFF - IR_Remote.CurrentTime + IR_Remote.LastTime;
                }

                if( IR_Remote.HeadFlag )
                {

                        if((IR_Remote.PulseTime > 0x4500) && (IR_Remote.PulseTime < 0x5200))      //time = 2.25ms, cnt  = 17@128us; bit 1  code
                        {
                                IR_Remote.RxBit |= 0x01;
                IR_Remote.BitCnt++;

                        }   
                        else if((IR_Remote.PulseTime > 0x2500) && (IR_Remote.PulseTime < 0x2A00))  //time  = 1.125ms, cnt = 8@128us, bit 0  code                  
                        {
                                IR_Remote.RxBit &= 0xFFFFFFFE;
                                IR_Remote.BitCnt++;

                        }
                        else //error pulse, reset
                        {
                                IR_RemoteInit();
                        }

                        if(IR_Remote.PulseTime)
                        {
                IR_Remote.PulseTime = 0;  


                if( IR_Remote.BitCnt >= 32)
                {
                    IR_Remote.UserCode    = (IR_Remote.RxBit >> 16 ) & 0xFF;   
                    IR_Remote.UserCodeInv = (IR_Remote.RxBit >> 24 ) & 0xFF;   
                    IR_Remote.DataCode    = (IR_Remote.RxBit >> 0 ) & 0xFF;   
                    IR_Remote.DataCodeInv = (IR_Remote.RxBit >> 8 ) & 0xFF;   

                    IR_Remote.RxDoneFlag = FALSE;
                    if( ( IR_Remote.UserCode + IR_Remote.UserCodeInv == 0xFF )
                    &&( IR_Remote.UserCode == IR_Remote.ID ) )         
                    {
                        if( IR_Remote.DataCode + IR_Remote.DataCodeInv == 0xFF )
                        {
                            IR_Remote.RxDoneFlag = TRUE;
                                                        SEND_MSG(IR_REMOTE_RX_DONE_MSG);
                                                        //printf("IR_Remote.DataCode = 0x%02xrn", IR_Remote.DataCode); //debug
                                                       
                        }
                    }

                    IR_Remote.HeadFlag = FALSE;
                    IR_Remote.BitCnt = 0;
                }
                else
                {
                    IR_Remote.RxBit <<=  1;
                }
                        }
                }
                else   
                {
            if(( IR_Remote.PulseTime > 0xB000) && (IR_Remote.PulseTime < 0xB500 ))   //time  = (9+4.5)ms, cnt = 105@128us header code        
            {                              
                IR_Remote.HeadFlag = TRUE;
                IR_Remote.BitCnt   = 0;
                IR_Remote.RxBit    = 0;
            }
            else
            {
                IR_Remote.HeadFlag = FALSE;
            }

            IR_Remote.BitCnt    = 0;  
            //IR_Remote.RxBit    = 0;            
            IR_Remote.PulseTime = 0;
                }
        }
        else
        {               
        //do nothing
        }       
}


补一下接收和接口图
2.jpg


20150623_221229.jpg



回帖(2)

辉辉在飞12138

2015-7-7 12:54:15
楼主你这是做好了干啥用?
举报

韦声健

2015-7-7 14:21:48
引用: 辉辉在飞12138 发表于 2015-7-7 12:54
楼主你这是做好了干啥用?

这种灯是有16种单色,四种功能,现在市面上的一般是红外和433M无线遥控,主要是商业使用,晚上店铺、公园等各种颜色的灯,有10W、20W、30W、60W,我以帮一个公司做的项目,通过这个板子可以实现手机控制,我想试试看,先实现红外遥控,然后再实现手机遥控
举报

更多回帖

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