⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 main.c

📁 本程序给出了2008年参加飞思卡尔智能车竞赛的完整的源程序
💻 C
字号:
#include <hidef.h>      /* common defines and macros */
#include <mc9s12dg128.h>     /* derivative information */
#pragma LINK_INFO DERIVATIVE "mc9s12dg128b"
#include "init.h"


#define FK        38          //原来为40,现在改为30
#define LINE      FK          //列为30,即每行采集30个点
#define HANG      20          //采20行 ,当改变行数的时候还需要只改变行中断里的数
#define LINE_MAX  2*LINE      //?
#define yuzhi     0x27        //设定二值化的阈值
#define accm      380         //acc mid,acc的中间值

void time_init(void);             //中断初始化函数
void isr_hinit(void);             //行初始化
void isr_cinit(void);             //场初始化
void shortdelay(unsigned int t);  //短延迟
void sciw(unsigned char ch);      //串口发送数据
void coms_int(void);              //摄像头初始化
void position(void);
void pwmout(void);
void senddata(void);
void key_init(void); 
void qpx(void);

unsigned int num=0,h=0,state=0;     //num:行脉冲计数器,h:行计数器,coms_int:行消隐的标志
unsigned char image_date[HANG][LINE]; //采集到的图像数据
unsigned char hang_date[LINE_MAX];
unsigned char qipx[38];         //起跑线
signed int acc=0,acc1,acc2=300,acct; //lei jia
unsigned char odd=0;            //odd is 奇场标志
unsigned char blp=19;           //blank line position
uint  spabs;                    //speed  absolute value
uint  dmotor;
uchar blank[HANG],blank19=19;
uchar crflg,crflg_temp=0;       //检测到交叉线标志
uchar pflag,qflag=0;            //position子函数里面的标志位,判断第一行是否有效
uchar led=0x0f;   
uchar xishu=26;                 //系数是用来调节小车的灵敏度的
uint  speed=0;                  //按键控制速度
uchar  i=0;                     //用于实时时钟产生定时信号
uint sudu ,lucheng=0;           //用来计算路程
uchar bcount=0,bcmax=0;         //黑点计数器,判断交叉线和起跑线,每场都需对这两个数清零
uchar a,zmax;
uint duoji=1500,dianji=600;

/*********************主程序******************/
void main(void) 
{
 //uchar a,b;                  //行和列计数器
  mcu_init(); 
  sci_init();      
  time_init();                 //初始化定时器,设置为输入捕捉
  init_pwm();
  key_init();
  init_counter();
  rti_init();                  //实时时钟初始化
  EnableInterrupts;
  
  DDRH=0xff;
  DDRA=0xff;
 
  PACTL_PAEN=1;               //脉冲累加器初始化,使能
  PACTL_PAMOD=0;              //模式选择
  PACTL_PEDGE=0;              //边沿检测方式
 
  PTH=0XFF;

  for(;;)
   {
      
       coms_int();            //coms_int里面有一个标志位,只有当采集数据完成才会执行下面代码             
       //senddata();  
       position();            //控制输出的部分 
       pwmout();
       qpx();
    }
    
 } 




/****************黑线识别和处理****************/
/*注意:对所有行,第一列和后三列无效,剩下的重新排序*/
void  position(void) 
 { 
   char x,y,youxiao=0;
  
   
   pflag=1;              //pflag是是否检测到19行的标志位,因为19行作为一个参考量;
                         //没有检测到必须说明
   if(crflg==0)          //遇到交叉线则crflg=1;那么就保持上一场的结果 
   {                               
     acc=0;
     for(y=3;y<LINE-3;y++)   //判断最靠近车体一行的黑线位置
      if((image_date[HANG-1][y]==1)&&(image_date[HANG-1][y+1]==1))
        if((y-blank19<10)&&(blank19-y<10))
         {              //blank19作为参考量,如果两场的黑线位置差值大于15,则最靠近车体的一行数据无效
           blank[HANG-1]=y;
           blank19=y;
           pflag=0;
           break;
         }                                                          
  
     for(x=HANG-2;x>=0;x--)  //判断除最靠近车体一行以外的所有行的黑线位置
       for(y=3;y<LINE-3;y++)
        {
          if((image_date[x][y]==1)&&(image_date[x][y+1]==1))
          {
            blank[x]=y;
            break;
          }
        }
        
        
   
     /******************对黑线位置的去噪和累加计算acc值*********************/
      for(x=HANG-2;x>=0;x--) 
       {
         if( ((blank[x]-blank[x+1])<3)&&((blank[x+1]-blank[x])<3) )
          {
              youxiao++;    
          } 
         else 
         {
          blank[x]=blank[x+1];
         }
             
            
           if((x>5)&&(x<16))  //这里只用第6行到15行的黑线位置来控制舵机,比较稳定一些
            {
              acc+=blank[x]; //累加部分反映了小车偏离赛道的程度,下
            }                 //面的斜率控制部分反映了赛道的方向趋势
       }

       
     /*************图像校正和加斜率控制****************/
      for(x=0;x<HANG;x+=3)
         {
           if(blank[x]<19)
             blank[x]=(19-(19-blank[x])/(1+x*0.05));       //以前是0.06
           else 
             blank[x]=(19+(blank[x]-19)/(1+x*0.05));
  
         }
                                           
    
     acc=acc+(blank[6]-blank[HANG-5])*30;   //斜率控制,只用了第6行和15行,比较稳定
                                            //这里acc的加法反映了黑线的斜率对舵机的控制作用
     acc=acc*2.0;                                       //考虑到斜率具有微分作用,所以控制舵机的算法中暂
                                            //时不用加微分了
   } 

 }
    

             
/***********舵机和驱动电机输出控制**************/
void pwmout(void) 
{ 

  duoji=1500+(acc-accm)*10/xishu;          //accm初始化为380,代表黑线位置在中间时的值
                                           //xishu初始化设置为30
  
  dianji=300;
  if(sudu>30)              //25            //sudu是速度,不高于30,不低于20
  {
    dianji+=100;
    //dianji=700;
  } 
  else if(sudu<20)         //18
  {
    dianji-=100;
    //dianji=280;
  }

  
  if(duoji<1300)  duoji=1300;             //左偏值最小为1300
  if(duoji>1700)  duoji=1700;             //右。。。。为1700

  PWMPER01=20000;
  PWMDTY01 =duoji;          /* 设置舵机角度,acc的最大值为6000左右 */ 
                        
  PWMPER67=1000;
  PWMDTY67=dianji+speed;    /* 设置电机速度, 越大越慢 */ 
  
  if(qflag==0)              //qflag是停止线标志,在下一个函数赋值,遇到终止线就等于1
  {
     PWME = 0x82;           //PWM输出
  }              
  else PWME=0;              //关PWM输出
}


/***********起跑线识别函数*************/
void qpx(void)
{
   
   if(lucheng>2400)         //路程计数器的值大于设定值以后,如果某一行的黑点数再大于预设值
   {                        //( crflg_temp==1,在行中断里面处理 )就认为是终止线,
     if(crflg_temp==1)      //交叉线标志,在行处理中断里面置位或清零
     {
        PTH=0XFF;           //LED灯熄灭表示检测到起跑线,
        // CRGINT_RTIE=1;   //禁止实时时钟
        qflag=1;            //qflag置1,
        crflg_temp=0;
     }
   } 
   else if(lucheng<300)      //因为刚启动时加速一段时间就会遇见起跑线,此时有一个路程累加误差
   {                         //应该在此清零
     if(crflg_temp==1)
     {
       PTH=0x00;
       lucheng=0;            //路程清零

     }
   } 
   
  else 
  {       
    PTH=led;
    led=~led;
  }
 
}

/***************向串口发送数据***************/
void  senddata(void)
{
   uchar a,b;
   for(a=0;a<HANG;a++)
   { 
     for(b=3;b<=LINE-3;b++)
     {
        sciw(image_date[a][b]);
     }
        sciw('b');
        sciw('b');
        sciw('b');
   }
}


/***************串口发送数据子函数*****************/
void sciw(unsigned char ch)
  {
    SCI0DRL=ch;             //发送数据
    while(!SCI0SR1_TC);     //判断是否发送完毕
  }

/*===============初始化定时器===============*/
void time_init(void)
  {
    TIOS=0X00;             //选择t所有通道为输入捕捉
    TSCR1=0X80;            //定时器时能
  }
  
/**************摄像头初始化,实际上开场中断,进入新的一场图像采集**************/
void coms_int(void)      
  {  
     state=0;            //state标志位保证采集到图像以后才会进行数据处理                      
     isr_cinit();        //初始化场中断
     while(!state);      //coms_int=1时为消隐时间,那么还要对coms_int取非吗? 
  }
  
  
/* ======场中断初始化 PT_1口 下降沿=======*/
void isr_cinit(void)
  {
     TCTL4_EDG1A=0;
     TCTL4_EDG1B=1;     //输入捕捉检测方式,捕捉下降沿,这里采集的是偶场信号
     TIE_C1I=1;         //定时器1开中断
     TFLG1_C1F=1;       //清中断标志
     DLYCT=0X01;        //延迟时间选择
  }
    
/*===============关闭场中断===============*/
void close_chang(void)
  {
     TIE_C1I=0;         //禁止场同步中断
  }                    
 
/*================场中断处理==============*/
#pragma CODE_SEG __NEAR_SEG NON_BANKED
void interrupt 9 ISR_chang(void)
  {
    num=0;              //清零行中断计数器,避免采到场消隐无效数据
    h=0;                //图像行清零
    
    TFLG1_C1F=1;        //清空中断标志位 
    //close_chang();    //原来是可以不关场中断的,数据不会遭到破坏,亦可以利用前35行的无效时间
    bcount=0;bcmax=0;   //用以计数某行的黑点数,判断是否交叉线或起始线
    crflg=0;            //每场对交叉线标志清零
    isr_hinit();        //启动行同步中断
  }
#pragma CODE_SEG DEFAULT   


/*==========行中断初始化 PT_0口===========*/  
void isr_hinit(void)
  {  TCTL4_EDG0A=0;      //输入捕捉边沿设置,将通道0设为上升沿捕捉
     TCTL4_EDG0B=1; 
     TFLG1_C0F=1;        //清除0通道中断标志
     TIE_C0I=1;          //0通道中断使能
  }
  
/*===============关闭行中断===============*/
void close_hang(void)
  {
     TIE_C0I=0;          //关0通道中断
  }
  
  
/*================行中断处理==============*/  
#pragma CODE_SEG __NEAR_SEG NON_BANKED
void interrupt 8 ISR_hang(void)
  {
    int k,j,yushu;
    TFLG1_C0F=1;                  //清中断标志位
    
    num++;                        //行中断脉冲个数
    if((num>=36)&&(num<=274))     //原来两个值为42和285,这里改为42和225
      {          
        yushu=num%12;             //到的数据滞后,可能是因为指令消耗时间太多
        if(yushu==0)
          {
            //shortdelay(60);     //做过简单的测试,延迟1大概为0.15us
            adc_init();
             for(k=0;k<=LINE_MAX;k++)
              { 
                while(!ATD0STAT1_CCF0);
                hang_date[k]=ATD0DR0L;
              }
             ATD0CTL2=0X00;
          }
         else if(yushu==1)            //把值存入到RAM里面
          {
             for(k=0,j=0;k<=LINE-1;k++,j+=2)
              {
                 image_date[h][k]=hang_date[j];
              } 
              
          }
         else if (yushu==2)           //进行二值化处理
         {
            for(k=3;k<LINE-2;k++)
             (image_date[h][k]>yuzhi)?(image_date[h][k]=0):(image_date[h][k]=1);  
             
            // h++;   
         } 
         
          else if(yushu==3)            //判断是否是起跑线
         {
            if((h>13)&&(h<19))
           {
             bcount=0;
             
              for(k=3;k<LINE-3;k++)
               {
                if((image_date[h][k]==1)||(image_date[h-1][k]==1))
                 {                    //这里对两行的数据1相或,防止小车跑歪时检测不到交叉线
                    qipx[k]=1;
                    
                    bcount++;  
                 }
               }
                       
              if(bcount>bcmax) bcmax=bcount; 
              if(bcmax>10) crflg=1;   //如果检测到的黑点数大于6个,则说明为交叉线
                                      //这句话在这是不行的,很可能会造成交叉线标志丢失,
           }                          //应该放在场中断里面清标志位
           h++; 
         }    
      }
   if(num>=275)                       //当改变行数时,这里也要改
      {
        crflg_temp=crflg;
        state=1;
        close_hang();
      }
  }
#pragma CODE_SEG DEFAULT  


/*==============延时函数=================*/
void shortdelay(unsigned int t)
  {
    while(--t);
  }
  

/*****************按键程序****************/
void key_init(void)                                                                   //
  {                                                                                   //
    DDRJ=0X00;                                                                        //
    PIEJ_PIEJ0=1;                                                                     //
    PIEJ_PIEJ1=1;                                                                     //
    PIFJ_PIFJ0=1;                                                                     //
    PIFJ_PIFJ1=1;                                                                     //
     
  }
#pragma CODE_SEG __NEAR_SEG NON_BANKED
void interrupt 24  key_isr(void)
{
  if(!PTJ_PTJ1)
  {
    PIFJ_PIFJ1=1;
    if(!PORTK_BIT5)
    { //key1
      //do sth.
      speed+=50;
      //if(speed>5000) speed=0;     
    }
    else
    { //key2
      //do sth.
      speed-=50;
    }
  }
  else 
  {
    PIFJ_PIFJ0=1;
    if(!PORTK_BIT4)
    { //key3
      //do sth.
      xishu++;                  //xishu 系数是调节小车灵敏度的,初始化设置为30
    }
    else
    {
      //key4
      //do sth.
      xishu--;
    }
  }
}
#pragma CODE_SEG DEFAULT


#pragma CODE_SEG __NEAR_SEG NON_BANKED

  void interrupt 7 isr_rti(void) 
  {
    CRGFLG_RTIF=1;           //清中断标志位
    i++;
    if(i>=4)
    {
      sudu=PACN32;
      lucheng+=PACN32;
      PACN32=0;
      i=0;
    }
  }
  
 #pragma CODE_SEG DEFAULT
 
 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -