13 算法/分析方法的优化以及总结

来源:互联网 发布:网络协议转换 编辑:程序博客网 时间:2024/06/07 11:29

(1)随机碰撞的优化以及写法

在博主以前的项目中,避免碰撞的思想主要是开启一个定时器2-Time02用来判断总线是不是忙,实现手段是初始化的时候定时器2关闭,当收中断口有数据传输的时候,进行定时器的开启,如果经过了一个延时时间,例如一个长度为30字节的帧,那么此时的延时时间定为31ms左右(波特率9600),定时中如果没有其他的帧在传输,则经过一个31ms后自己发送数据。这个算法主要是想在一个完整的空闲时间后进行传送帧的发送。但是实际效果非常差,数据帧的传输碰撞概率十分大,在博主的测试中,7台的数据发送由于设备少而且随机数的间隔比较大,所以情况并不是十分明显,当设备增加到12台并且发送间隔缩小到1min左右,随机数的错开已经没有什么用了,此时碰撞算法的需求性就显现出来了。

在讨论后发现原来的算法没起作用的原因是那么新的算法优化想法是虽然中断收数据的时候开启定时器,但是没有具体的分别出一帧一帧的间隙时间,那么如果可以在一个拥挤的时间内分析出一帧一帧的间隔,然后将定时器2与间隔时间进行对比,那么这个碰撞算法的实现就可以说比较成功了。

所以在新版本的代码中,博主使用了一个变量flag进行判断一帧一帧的区别。

interrupt [USART_RXC] void usart_rx_isr(void)
{
    char status,data;
    status=UCSRA;                              
    data=UDR;
    flag=1;           //判断帧间隔的flag
    t2cnt=0;
    TCCR2=0x06;//定时器2打开
    if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0) //状态收中断没有问题后
    {   
        rxbuf[rxcnt++]=data;     
        if(rxcnt>49)rxcnt=0;
    }   
}

定时器2中当一帧结束后使用该变量进行赋值,具体的调用在主函数发送之前。

interrupt [TIM2_OVF] void timer2_ovf_isr(void)   //定时器2用于收中断来判断总线是不是忙
{
    TCNT2=0xE1;
    t2cnt++;
    if(t2cnt>10)
    {   
        t2cnt=0;
        flag=0;//到达间隔时间后
        sendok=1;
        TCCR2=0x00;
    }
}

并且在主函数进行falg的判断,如果经过了一帧,则使用定时器进行判断

void main(void) 

{          。

           。

           。//以上为发送数组缓存区的赋值和帧格式的填充

            while(flag);                      
            TCCR2=0x06;
            while(!sendok);
            tx;
            UDR=txbuf[0];
}

此时当两个要求都满足的时候,则进行数据发送,该想法通过项目测试真实可行,有效的避免了碰撞的发生。

(2)发送数据的延时问题

这个问题的发生主要是由于AVR单片机的发送引脚和接收引脚的物理置位快于数据帧的数据传输,所以导致数据传输的不完整性。

代码示例:

                Tx;
                uart_sendndata(tx0buf,0,50);
                uart_sendndata(tx0_1buf,0,32);
                Rx;

方法示例:

                void uart_sendndata(u8 str[],u16 m,u16 n)
               {
                     u16 i;
                     for(i=m;i<n;i++)
                     uart_senddata(str[i]);    
               }

该方法的作用是对数组进行发送,但是如果由上面的代码进行发送的话,会导致发送的帧没有到达32字节,可能在30或者31个字节的时候就被强迫停止发送而导致一帧的不完整性。具体的处理方法是使用延时进行处理,保证一帧的数据传输完毕。

                Tx;
                uart_sendndata(tx0buf,0,50);
                delay_ms(5000);
                uart_sendndata(tx0_1buf,0,32);
                delay_ms(100);
                Rx;

(3)温度寿命不回回退的优化算法

该算法实现的功能是设定一个门限值,当传感器传回的值超过门限值该器件的寿命变量折损为50%,超过1.2倍门限值时,寿命折损为40%,等等。。。这里有一个要求就是当传感器传回的门限值回复的时候,即回复到门限值一下的时候,寿命不能进行回复。

原来博主写的方法虽然可以实现,但是代码较为繁琐,而且变量设定的特别多:

extern eeprom u8 one,two,three,four,fivesflag,sixsflag;//这里的变量作为一个一次性变量,起到的作用是“连环锁”作用,开启一环锁一环,这样防止数据的回退

void temstacheck(void)
{  
    u8 index=0;
    temvalue; //温差数值
   if(大于门限值)
   {  
        {   
            temcnt1=0;   
            if((temvalue>wddifference)&&(one==1))index=1;
            if((temvalue>1.3*wddifference)&&(two==1))index=2;
            if((temvalue>1.6*wddifference)&&(three==1))index=3;      
            if((temvalue>1.8*wddifference)&&(four==1))index=4;
            if((temvalue>1.9*wddifference)&&(fivesflag==1))index=5;
            if((temvalue>2*wddifference)&&(sixsflag==1))index=6;
            switch(index)
            {   
                case 1: wdvalue=liehuavalue*0.5,one=0,two=1; break;   
                case 2: wdvalue=liehuavalue*0.4,two=0,three=1; break;                        
                case 3: wdvalue=liehuavalue*0.3,three=0,four=1; break;                      
                case 4: wdvalue=liehuavalue*0.2,four=0,fivesflag=1; break;
                case 5: wdvalue=liehuavalue*0.1,fivesflag=0,sixsflag=1;; break;
                case 6: wdvalue=liehuavalue*0.02,sixsflag=0; break;          
                default:break;
            }    
         }
      } 
}                                
在参见了带博主的工程师的代码后,博主的写法进行了一些改善

void temstacheck(void)
{
    u8 index=0;
    temvalue; //温差数值
    
//    if(temvalue>5)  //大于5℃
    if(大于门限值) 
    {   
        if(++temcnt1>4)        
        {   
            temcnt1=0;
            if(ldlvalue<ldlmenxian)index=0;
               。。。

               。。。

            if(ldlvalue>3*ldlmenxian)index=9;

            switch(index)
            {   
                case 0: liehua_b=liehua_a*1;   break;
                。。。

                。。。

                case 9: liehua_b=liehua_a*0.05;break;                
                default:break;
            }    
        }
    }   
    else
    {   
        temcnt1=0;
        liehua_b=liehua_a;  
    }
然后在主函数中进行变量的赋值与更新

                if((liehua_c>liehua_b)&&(liehua_b!=0))  //增加liehua_b!=0,避免初始化
                    liehua_c=liehua_b;    
                if(liehua_c!=0)
                    liehua_spd=liehua_c;   
                if(sta1)
                    liehua_c=0;
                else
                    liehua_c=liehua_spd;

这样也可以有效的避免了数值的回退问题。

(4)ADC随机采集三位小数的后两位

这个“古老”的问题源于取随机数的“伪随机”功能,原因很简单,在不管管脚由上拉电阻或者下拉电阻确定电平还是管脚浮空,在一定的采集速度下,可能采集出来的小数点后两位由固定几个值组成,这样可以看做我们产生了一个“伪随机”的数组,这样的数值放入随机种子中最后的结果也是不尽人意的。

但是在项目的实施过程中我们发现,虽然小数点后两位的变化可能就是那几个固定的数值,但是三位小数的后两位可以说是真正的实现了随机,所以在项目的过程中我们就要想办法吧3位小数的后两位进行采集,这里博主的方法是取余,对100取余,因为我们采集的小数例如3.345,在将小数取出来后数值为345,再对100取余,即可以去除后面的随机数。

实现方法:

取出3位小数

adctemp=(read_adc(0x43)-(int)read_adc(0x43))*1000;

然后取余

 random=adctemp%100;

(5)帧格式代码上传中使用if..else..代替switch语句

  if(((rxbuf[6]<<8)+rxbuf[5])==crctemp)
                        {
                            rxcnt=0;
                            if(rxbuf[2]==0x01)
                                sendbasicflag=1;                            
                            else if(rxbuf[2]==0x03)
                            {
                               
                            }
                            else if(rxbuf[2]==0x04)
                            {
                               
                            }
                            else if(rxbuf[2]==0x06)
                            {
                              
                            }
                            else if(rxbuf[2]==0x07)
                            {
                               
                            }
                            else
                                #asm("nop")

                            }

最后使用空指令

0 0