📄 main.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 + -