我做的是一个直流电动机转速和转向的控制。现在就是电动机的转向还没办法控制。大神哥哥姐姐帮忙看一下吧。谢谢啦
#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; //停止键
vola
tile 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,按键太快
}
}
}
-
-
-
最终(程序和图).rar
下载积分: 积分 -1 分
48.59 KB, 下载次数: 2, 下载积分: 积分 -1 分
一周热门 更多>