为什么我已经让P0.25拉低,仿真的时候p0.25口的电平却是不确定的呢,求大神解决

2019-07-16 08:31发布

我做的是一个直流电动机转速和转向的控制。现在就是电动机的转向还没办法控制。大神哥哥姐姐帮忙看一下吧。谢谢啦
#include"LPC21xx.h"
typedef  int int32;//有符号的
typedef unsigned char uint8;
typedef unsigned int uint32;//无符号的
//LCD用的端口
#define rs (1<<8)
#define rw (1<<9)
#define en (1<<10)
#define busy (1<<7)
//键盘用
#define ROW1 (1<<16)//行线1P1.16
#define ROW2 (1<<17)
#define ROW3 (1<<18)
#define ROW4 (1<<19)
#define COL1 (1<<20)//列线1
#define COL2 (1<<21)
#define COL3 (1<<22)
#define COL4 (1<<23)

#define MIN_PULSES  6
#define MAX_PULSES  1785
#define MAX_PWM  16000//周期
#define MIN_PWM  1

/* 系统设置, Fosc、Fcclk、Fcco、Fpclk 必须定义*/
#define Fosc 11059200                    //晶振频率,10MHz~25MHz,应当与实际一至
#define Fcclk (Fosc * 4)                 //系统频率,必须为Fosc 的整数倍(1~32),且<=60MHZ
#define Fcco (Fcclk * 4)                 //CCO 频率,必须为Fcclk 的2、4、8、16 倍,范围156MHz~320MHz
#define Fpclk (Fcclk / 4) * 1         //VPB 时钟频率,只能为(Fcclk / 4)的1 ~ 4 倍 通常只需要设置Fosc 即可。需要更改Fcclk 和Fcco 时,参照注释。
/*上面这一行非常重要,设置外部时钟频率为11.0592Mhz,设置之后才能给定时器计数
//本来计数11059200为1s,100分频后计数110592为1s         */

//PI调节用到
#define P_ref  1
#define I_ref  2
//PI环节要用
int32 P_val=0;
int32 I_val=0;
int32 D_val=0;
int32 T_val=0;
int32  changeref=1;

//下面都是字符串的显示
uint8 dir[4]={'+','','-',''};                         //转向显示
uint8 rpm[]={"rpm"};                                                //转速单位
uint8 current[]={"Current:"};                                //目前
uint8 seted[]={"  Seted:"};                                        //设置
uint8 enternum[]={"Please set the rpm:"};        //请设置转速
uint8 strcrt[5];
uint8 strset[5];

uint8 flag=1;                                                                 //flag=1,表示开始等待设置转速
                                                                              //0应显示目前的转速,转向

uint8 shutdown=0;                                                        //停止键
volatile uint8 Key;                                                        //全局变Key()原来是key=16
uint8 keyhit=16;
uint8 dirhit=0;                                                                //转向设置,0为‘-’,1为‘+’

uint8 keydown=0;                                                        //键盘关闭键(判断是否有键按下)
uint8 downindex=0;                                                        //按键的位数

uint8 dircrt=0;                                                                //当前的转向
int32 varcrt=0;                                                                //当前的转速

int32 varset=(int32)0;                                                //准备设置的转速
int32 keysum=(int32)0;                                                //按键的计算值=转速

uint32 rtcounts=0;                                                        //脉冲计数

int32 setcounts=0;                                                        //设置的转速对应的脉冲频率
int32 deltacounts=0;                                                //根据计数得到的脉冲 算出的 脉冲频率
int32 pwmdata=0;                                                        //高电平的时间,它除以MAX_PWM(周期)=占空比


uint8 key=16;//(改过不能在变,当获取到的key为16时表示没有键按下)
/********************************************************************
功能:设置系统各部分时钟PLL初始化
****************************************************************** */
void PLL_Init(void)
{
        PLLCON = 1;
        #if ((Fcclk / 4) / Fpclk) == 1
        VPBDIV = 0;
        #endif
        #if ((Fcclk / 4) / Fpclk) == 2
        VPBDIV = 2;
        #endif
        #if ((Fcclk / 4) / Fpclk) == 4
        VPBDIV = 1;
        #endif
        #if (Fcco / Fcclk) == 2
        PLLCFG = ((Fcclk / Fosc) - 1) | (0 << 5);
        #endif
        #if (Fcco / Fcclk) == 4
        PLLCFG = ((Fcclk / Fosc) - 1) | (1 << 5);
        #endif
        #if (Fcco / Fcclk) == 8
        PLLCFG = ((Fcclk / Fosc) - 1) | (2 << 5);
        #endif
        #if (Fcco / Fcclk) == 16
        PLLCFG = ((Fcclk / Fosc) - 1) | (3 << 5);
        #endif
        PLLFEED = 0xaa;
        PLLFEED = 0x55;
        while((PLLSTAT & (1 << 10)) == 0);
        PLLCON = 3;
        PLLFEED = 0xaa;
        PLLFEED = 0x55;       
}   
/****************************************************************************
* 功能:简单延时(不动)
****************************************************************************/
void delay(int ms)                                                         
{                                                                          
   int i;                                                                  
   while(ms--)                                                            
   {   for(i = 0;i<250;i++){}   }                                                                    
}
/*****************************************************************
功能:等待LCD,检查总线是否忙 (改过了不动)
******************************************************************/
void waitLCD()                          
{
  IO0DIR=0xf00;                      //1111 0000 0000,设置p0.0-p0.7均为输入,p0.8-p0.11为输出
  while(1)
  {
   IO0CLR = rs;                      //p0.8清零,复位
   IO0SET = rw;                      //p0.9置位,写信号
   IO0SET = en;                      //p0.10置位,使能芯片
   if(!(IO0PIN & busy))break;        //如果读取的端口的p0.7为0,则退出该while循环
   IO0CLR = en;                      //p0.10清零
  }
  IO0DIR = 0xfff;                    //1111 1111 1111 设置p0.0-p0.11为输出
  }

/****************************************************************************                                                                          
功能:写函数(核对过)
****************************************************************************/
void WrOp(uint8 dat)//送LCD控制码
{
        waitLCD();
        IO0CLR=rs;                //全部清零
        IO0CLR=rw;
        IO0CLR=0xff;                //先清零
        IO0SET=dat;                //再送数
        IO0SET=en;
        IO0CLR=en;
}
/****************************************************************************
* 功能:写数据函数(核对过)
****************************************************************************/
void WrDat(uint8 dat)       
{
        waitLCD();
        IO0SET=rs;
        IO0CLR=rw;
        IO0CLR=0xff;                //先清零
        IO0SET=dat;                //再送数
        IO0SET=en;
        IO0CLR=en;       
}      
/****************************************************************************
* 功能:lcd初始化函数(核对过)
****************************************************************************/
void lcd_init(void)
{
        WrOp(0x38);                        //16*2显示,5*7点阵,8位数据      
        WrOp(0x06);                        //光标加1
        WrOp(0x0c);                        //开显示,关光标
        IO0DIR |=0x000007ff;                //设置为输出
        IO0CLR=0x7ff;
}
/****************************************************************************
* 功能:显示文本函数(核对过)
****************************************************************************/
void DisText(uint8 addr,uint8 *p)
{
        WrOp(addr);
        while(*p !='')    WrDat(*(p++));
}  
/****************************************************************************
功能:浮点数值转换为字符串函数,5.2格式(核对过)
****************************************************************************/
void Int2Str(unsigned char str[],int32 var)
{
        str[0]=(unsigned char)((((int32)(var)/1000)%10)+48);
        str[1]=(unsigned char)((((int32)(var)/100)%10)+48);
        str[2]=(unsigned char)((((int32)(var)/10)%10)+48);
        str[3]=(unsigned char)((((int32)(var))%10)+48);
        str[4]='';
}
/*二、键盘程序(暂时不动,已实现功能)*/
//按键的扫描,扫描结束后返回按键的代码Key
uint8 key_scan(void)
{        uint8 key0 = 16;
    uint32 i,key1,key2,Key;
        //扫描第一列
        IO1CLR = COL1;                                                        //列线COL1输出置低
        IO1SET = COL2|COL3 | COL4;                                //其他列线置高
        //扫描第一列
        IO1CLR = COL1;                                                        //列线COL1输出置低
        IO1SET = COL2| COL3 | COL4;                                //其他列线置高
        if((IO1PIN & ROW1) == 0)  key0 = 0;                //如果按键发生在第一行
        if((IO1PIN & ROW2) == 0)  key0 = 4;
    if((IO1PIN & ROW3) == 0)  key0 = 8;                //如果按键发生在第3行
        if((IO1PIN & ROW4) == 0)  key0 = 12;
    //扫描第2列
        IO1CLR = COL2;                                                        //列线COL1输出置低
        IO1SET = COL1| COL3 | COL4;                                //其他列线置高
        if((IO1PIN & ROW1) == 0)  key0 = 1;                //如果按键发生在第一行
        if((IO1PIN & ROW2) == 0)  key0 = 5;
    if((IO1PIN & ROW3) == 0)  key0 = 9;                //如果按键发生在第3行
        if((IO1PIN & ROW4) == 0)  key0 = 13;
    //扫描第3列
        IO1CLR = COL3;                                                        //列线COL1输出置低
        IO1SET = COL4| COL1 | COL2;                                //其他列线置高
        if((IO1PIN & ROW1) == 0)  key0 = 2;                //如果按键发生在第一行
        if((IO1PIN & ROW2) == 0)  key0 = 6;
    if((IO1PIN & ROW3) == 0)  key0 = 10;        //如果按键发生在第3行
        if((IO1PIN & ROW4) == 0)  key0 = 14;
    //扫描第4列
        IO1CLR = COL4;                                                        //列线COL1输出置低
        IO1SET = COL3| COL1 | COL2;                                //其他列线置高
        if((IO1PIN & ROW1) == 0)  key0 = 3;                //如果按键发生在第一行
        if((IO1PIN & ROW2) == 0)  key0 = 7;
    if((IO1PIN & ROW3) == 0)  key0 = 11;        //如果按键发生在第3行
        if((IO1PIN & ROW4) == 0)  key0 = 15;
    //四列全清零
    IO1SET = COL1 | COL2| COL3 | COL4;
                
        delay(20);                                                                //延时消抖
        key1 = key0;                                                        //按键扫描
        delay(20);
        key2 = key0;
        if(key1 == key2)
                Key = key1;                                                        //如果两次扫描得到的按键值相同,则保存这个按键
    return(Key);
}
/****************************************************************
3.按键初始化,(暂时不动,以实现功能)令列线输出低电平,行线输入
*****************************************************************/
void Key_init(void)
{        //行线ROW1-ROW4;列线COL1-COL4
        IO1DIR = (IO1DIR&(~ROW1)&(~ROW2)&(~ROW3)&(~ROW4)) | COL1 | COL2 | COL3 | COL4;
        IO1CLR = COL1 | COL2| COL3 | COL4;
}
/****************************************************************************
* 名称:Key_Process()改变keysum(转速),转向,启动,停止
****************************************************************************/
void Key_Process(uint8 num)        //num后面等于Key,Key又是从scan函数获取                           
{
switch(num)
         {
                case 0:keyhit=1;break;
                case 1:keyhit=2;break;
                case 2:keyhit=3;break;
                case 3:keyhit=10; dirhit=!dirhit;break;//转向设置,显示正负号
               
                case 4:keyhit=4;break;
                case 5:keyhit=5;break;
                case 6:keyhit=6;break;
                case 7:keyhit=11;  if(!flag) varset=(int32)(varset+1);//在监视状态置直接微调整转数(加速)
                                            else     keysum=(int32)(keysum+1);break;//在设置状态调整keysum
               
                case 8:keyhit=7;break;
                case 9:keyhit=8;break;
                case 10:keyhit=9;break;
                case 11:keyhit=12;  if(!flag) varset=(int32)(varset-1);//在监视状态直接微调整转数(减速)
                                                     else           keysum=(int32)(keysum-1);break;//在设置状态调整keysum
                case 12:keyhit=13;  shutdown=!shutdown;          //开关键
                                                if(shutdown) {flag=0;varset=0;}//监视状态,转为0转速
                                                else { flag=1; keysum=(int32)0; downindex=0; WrOp(0x01);}//设置状态
                                                break;// 强制将电机重启为初始值
                case 13:keyhit=0;break;
                case 14:keyhit=14; flag=1; keysum=(int32)0;  downindex=0; WrOp(0x01); break;//  进入设置模式
                case 15:keyhit=15; flag=0; varset=keysum;    WrOp(0x01);break; //确定输入,返回监视模式
                default:break;
         }
        if((keysum<0))keysum=0;
        if((varset<0))varset=0;
        if(varset>350)varset=350;// 最大转数限制
}
/*************************************************
功能:配合定时器0捕获脉冲
**************************************************/
void __irq catch_pulses(void)
{
        rtcounts++;                                                //脉冲数
               
        T0IR=0x20;                        //清除中断标志,复位CR1中断
        VICVectAddr=0x00;                 //通知VIC中断处理结束
}
/***********************************************************
功能:配合定时器1,每0.1s取一个脉冲数,计算目前的转速,占空比
***********************************************************/
void __irq PI_porcess(void)
{   
    deltacounts = rtcounts*10;                        //对脉冲速率(每秒的脉冲数)采样(目前的速率)
                                                         //rtcounts是0.1s采集到的脉冲
       
        setcounts = varset*5;                                //(60*转速/300),300是每转的脉冲数,
                                                                                //把设定的转速 转换为脉冲速率
                                                                            //电机的转速值(rpm)=(编码器的脉冲频率×60)/每转脉冲数
                                                                                //本系统中电机的转速值rpm=编码器的脉冲频率/5
       
        P_val=setcounts*P_ref*changeref;        //设定转速(脉冲速率)对应的高电平的时间

        //改变脉冲,没有误差的时候I_val不再变化
        /*if((setcounts-deltacounts)>0)
           if(I_val<MAX_PULSES)      I_val++;
        if((setcounts-deltacounts)<0)
           if(I_val>(-MAX_PULSES))   I_val--;   //脉宽

        T_val=I_val*I_ref*10*changeref;                        //脉冲频率 平均误差的累积和
        T_val=(int32)(T_val/10);                                //还原changeref的放大倍率

        if(T_val<MIN_PWM) T_val=MIN_PWM;                //对输出限幅
        if(T_val>MAX_PWM)T_val=MAX_PWM;
        pwmdata=(int32)T_val;*/                                        //获得占空比
        varcrt=deltacounts/5;                                        //把采样的脉冲速率转换为转速
        pwmdata=(int32)(P_val/10);
        rtcounts=0;

       
        T1IR=0x01;                        //清除中断标志,复位MR0中断
        VICVectAddr=0x00;                 //通知VIC中断处理结束
}
/**********************************************************
功能:定时器0捕获脉冲数
**********************************************************/
void capinit(void)                                  
{
        T0PR=5;                       //2个主频后TC加1
        T0CCR=(1<<3)|(1<<5);                   //和(0x028)相同,设置CAP0.1上升沿捕获,捕获后将TC的值放入T0CR1,并产生中断
        T0TC=0;
        T0TCR=0x01;                   //启动定时器
}
/*******************************************************
功能:定时器1中断,定时0.1s,取一次脉冲数
********************************************************/
void time1init(void)               
{
  T1PR=99;                 //设置定时器分频为100分频,得147450Hz
  T1MCR=0x03;              //匹配通道0匹配中断并复位T1TC
  T1MR0=110592/2/5;        //比较值,分母为Fosc,分子/2为0.5秒以此类推,
                           //此处为0.1秒,就是除以10

  T1TCR=0x03;              //启动并复位T1TC            
  T1TCR=0x01;  
}
/***************************************************
功能:PWM初始化(已经明白)
****************************************************/
void pwminit(void)
{
        PWMPR=0x00;                      //不分频,计数频率为Fpclk
        PWMMCR=0x02;                     //设置PWMMR0匹配时复位PWMTC
        PWMMR0=MAX_PWM;                  //设置PWM周期
        PWMMR5=1;                        //设置PWM占空比
        PWMLER=0x21;                     //PWMMR0,PWMMR5锁存
        PWMPCR=1<<13;                    //允许PWM5输出,单边PWM
        PWMTCR=0x09;                     //启动定时器,PWM使能64
}
/****************************************************************************
* 功能:显示文本
****************************************************************************/
int  main(void)
{   PLL_Init();                              //PLL初始化,设置时钟         
    PINSEL1=0x00800400;                        //引脚功能选择//p0.0-0.10都是GPIO,不用在设置
                                                                 //0000 0000 10(P0.27捕获0.1)00 00(p0.25GPIO)00 00(p0.23GPIO)00 01(P0.21PWM5)00 0000 0000
                                                                
        IO0DIR =0x020007ff;                        //对GPIO的方向设置,0入1出。
                                                                //001(P0.25方向开关 输出)0 0(p0.23转向 输入给芯片)000 0000 0000 (0111 1111 1111 输出 P0.0-p0.10控制LCD)
         
        changeref=(10*MAX_PWM )/ MAX_PULSES;//changeref被放大;

        lcd_init();                                                        //LCD的初始化
    Key_init();                                                        //键盘的初始化(包含了scan,扫描过的值改变了Key)
        pwminit();                                                         //PWM初始化
       
        capinit();                                                        //定时器0捕获脉冲
        time1init();                                                //定时器1定时0.1s
       
        VICIntSelect=0x00;                                        //全部设置为IRQ中断     
        VICVectCntl0=0x24;                                        //001(中断使能)0 0100分配的中断编号
        VICVectAddr0=(uint32)catch_pulses;  //中断到捕获脉冲
        VICVectCntl1=0x25;     
        VICVectAddr1=(uint32)PI_porcess;        //获得新的pwmdata
        VICIntEnable=0x00000030;                        //使能中断
       
        //pwmdata=MIN_PWM;                                        //(为1)设置一个初始的占空比
        IO0CLR =(1<<25);                                          //p0.25(控制方向)清零,设置初始方向电位

        while(1)
        {  
                key=key_scan();
                if((key!=16))        keydown=1;                        //有按键按下
                                else         keydown=0;
                Key_Process(key);                                        //键值对应的操作,修改flag,keysum,varset,dirhit,keyhit.
               
                //通过获得新的pwmdata,来改变占空比
                PWMMR0=MAX_PWM;                                                //设置PWM5的周期
                PWMMR5=pwmdata;                                                //设置占空比=T_val/10
                PWMLER=0x21;                                                 //PWMMR0和PWMMR5锁


                 if(!flag)//如果flag标志为0时,监视模式,改变转向之类的
                {         
                    //转向检测p0.23
                        if(IO0PIN&(1<<23))  dircrt=0;//p0.23为高电平时,为反转,显示“-”
                        else                                 dircrt=1;//低电平时,正转,显示“+”       
                        //转向开关p0.25
                        if(dirhit)         IO0SET =(1<<25);//如果dirhit为1令电机正转
                        else        IO0CLR =(1<<25);
                        //显示前面的提示符
                DisText(0x80,current);
                        DisText(0x91,rpm);
                        DisText(0xc0,seted);
                        DisText(0xd1,rpm);
                        //显示现在的转向
                        if(dircrt)   DisText(0x88,dir);       //"+"dircrt为1时,正转 监视的转向显示
                        else         DisText(0x88,(dir+2));  //"-"
                        //显示设置的转向
                        if(dirhit)   DisText(0xc8,dir);//设置的转向显示
                        else         DisText(0xc8,(dir+2));
               
                        Int2Str(strcrt,varcrt);//监视的转速显示
                        Int2Str(strset,varset);//设置的转速显示
                        DisText(0x89,strcrt);
                        DisText(0xc9,strset);
                        delay(100);
                }

                 else //flag为1时,设置模式,等待键入新的转速值
                {       
                    if((keydown)&&(keyhit<=9)&&(downindex<4))//最大键入4位
                        {       
                                keysum=(int32)(keysum*10+keyhit);//按键和值
                                downindex++;
                        }
                        DisText(0x80,enternum);//字符提示:键入的数值为
                Int2Str(strset,keysum);//用户输入的键值显示
                        DisText(0xc6,strset);
               
                        if(dirhit)         DisText(0xc4,dir);    //dir显示“+”设置的的转向显示
                        else                 DisText(0xc4,(dir+2));//显示"-"
                        
                        delay(2500);//原来是100,按键太快
                }
        }
}       

捕获ddd.PNG
最终(程序和图).rar 下载积分: 积分 -1 分
48.59 KB, 下载次数: 2, 下载积分: 积分 -1 分
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。