最近在做无刷电机控制器,用了PIC30f2010,也参考了官方的例子。开机运行时要慢慢启转动的,我是控制PDC的值,先为0再一直加到512。这个功能是行的,但是在过流时要把占空比调小,就是转速要慢,让它电流值在那个范围内,我是这样做的,一当有电流超过设定值就减一个PDC的值,电流如果没有到设定值就加一个PDC值,可到那运行时却变成调时基了,PDC的值为512时是没有时基的,511就有一点点时基了,时基频率是设定值,但脉冲很窄。这个值越小时基就越多,PDC明明是调占空比的怎么会变成调时基(PWM)去了呢?程序如下:写的有点乱,也比较笨,别见笑!
//----------------------------------------------------------------------
//**************** 振荡器 ************************************
//#define dFoscExt 5 000 000 // 外部晶振或时钟频率(Hz)
//#define dPLL 8 // PLL 比率
//#define dLoopTimeInSec 0.00005 // PWM 周期 - 100 uS, 10Khz PWM
//#define dDeadTimeSec 0.000 002 // 以秒为单位的死区时间
// Derived
//#define dFosc (5 000 000*8) // 时钟频率(Hz)
//#define dFcy (40 000 000/4) // 指令频率(Hz)
//#define dTcy (1.0/10 000 000) // 指令周期(s)=0.000 000 1s=100ns
//#define dDeadTime (int)(0.000 002*10 000 000) // 以dTcys 为单位的死区时间=0.000 000 000 0002
//#define dLoopInTcy (dLoopTimeInSec/dTcy) // 以Tcy 为单位的基本循环周期
//#define dDispLoopTime 0.100 // 显示和按钮状态查询循环
#include "p30F2010.h"
_FOSC(0x0c306); //XT振荡,4倍频晶振.
_FWDT(WDT_OFF); //关闭看门狗定时器
_FBORPOR(PBOR_OFF & MCLR_EN); //掉电复位禁止,MCLR复位使能。
_FGS(CODE_PROT_OFF); //代码保护禁止
#define FCY 10000000// xtal = 5.0Mhz ; PLLx8
#define MILLISEC FCY/10000// 1 mS 延迟常数
#define FPWM 39000
#define S2 LATCbits.LATC14
#define EN2 LATCbits.LATC13
#define RS2 LATDbits.LATD0 //15
#define HC595_SCLK LATDbits.LATD1 //14
#define HC595_SID LATFbits.LATF2 //18
#define HC595_enable LATFbits.LATF3 //17
void InitTMR3(void);
void InitADC10(void);
void AverageADC(void);
void DelayNmSec(unsigned int N);
void InitMCPWM(void);
void CalculateDC(void);
void GetSpeed(void);
struct {
unsigned RunMotor : 1;
unsigned Minus : 1;
unsigned xsbit : 1; //0.5S显示一次
unsigned Ysbit : 1; //电机刚起的时候的延时
unsigned tdybit : 1; //太低压了保护
unsigned dzbit : 1; //堵转的位
unsigned dlbit : 1; //短路的位
unsigned zfbit : 1; //正反转的位
unsigned addybit : 1; //ad低压
unsigned adglbit : 1; //ad过流
unsigned unused : 6;
} Flags;
unsigned int HallValue;
unsigned int Ys; //刚开电机时电压和电流不检测延时一下
unsigned int ysmoto,glms; //慢起动
unsigned int glys;
unsigned int gl[10];
unsigned int dy[10];
unsigned int dybit;
unsigned int glbit;
unsigned int i,j,k,l,m;
unsigned long glnum,dynum;
unsigned long vmoto,zsmoto,zsbit,zsjs;
unsigned char ZF[ ]={'U',':','.','V',' ','B','A','T','%','R','P','M'};
unsigned int test;
unsigned int dzbf,dzbfjs; //堵转保护
unsigned int ADAD,state;
/************************************************************/
void delay(unsigned int i)
{
while(i--);
}
/*************************************************************
以下是低端驱动器表。在此StateLoTable 中,
在低端驱动器施加PWM 信号,而高端驱动器为“导通”或“截止”状态。
在本练习中使用此表。
*************************************************************/
unsigned int StateLoTable[] = {0x0000, 0x02001, 0x0810, 0x0801,0x0204, 0x2004, 0x0210, 0x0000};
/****************************************************************
以下是变化通知引脚CN5、CN6 和CN7 的中断向量。
当霍尔传感器改变状态时,将引起中断,指令执行将转到下面的子程序。
然后用户必须读端口B 的第3 位、第4 位和第5 位,
对读到的值进行移位和调节以使之读作1、2……6。
然后将调整后的值用作查找表StateLoTable 中的偏移量
以确定装入OVDCON 寄存器的值。
*****************************************************************/
void _ISR _CNInterrupt(void)
{
IFS0bits.CNIF = 0; // 清零标志
HallValue = PORTB & 0x0038; // 屏蔽其它位,保留RB3、RB4 和RB5
HallValue = HallValue >> 3; // 执行3 次右移
OVDCON = StateLoTable[HallValue];
zsbit++;
dzbf=0;
//if(HallValue==1)
//S2=!S2;
}
/*********************************************************************
ADC 中断用给定的电位计值装载PDCx 寄存器。
仅在电机运行时执行此操作。
*********************************************************************/
void _ISR _ADCInterrupt(void)
{
IFS0bits.ADIF = 0;
if (Flags.RunMotor)
{
//ADAD=ADCBUF0>>1;
//ADAD=ADAD-ysmoto-glms;
//PDC1 = ADAD; // 赋值……>>等于/2笨蛋
//PDC2 = PDC1; // 并装载所有的三个PWM……
//PDC3 = PDC1; // 占空比寄存器
if(Flags.addybit==0){
dy[dybit]=ADCBUF1;
if(dybit++>=8)
{
dybit=0;Flags.addybit=1;
}
}
if(Flags.adglbit==0){
gl[glbit]=ADCBUF2;
if(glbit++>=8)
{
glbit=0;Flags.adglbit=1;
}
}
}
}
/*********************************************************************
外中断,下降沿触发,短路保护
*********************************************************************/
void _ISR _INT0Interrupt(void)
{
IFS0bits.INT0IF = 0;
//PWMCON1 = 0x0700; // 禁止PWM 输出
//OVDCON = 0x0000; // 将PWM 改写为低电平
Flags.RunMotor = 0; // 复位运行标志
Flags.dlbit=1;
}
/*******************************************************************************************************************/
//发送一个字节(底层函数)
void lcm_w_byte(unsigned char bbyte) {
unsigned char i;
for(i=0;i<8;i++){
HC595_SID=bbyte&0x01; //取出最高位
DelayNmSec(1);
HC595_SCLK=0;
DelayNmSec(1);
HC595_SCLK=1;
bbyte>>=1; //左移
HC595_enable=0;
DelayNmSec(1);
HC595_enable=1;
DelayNmSec(1);
}
}
/*************************************/
void enable2(void)
{
EN2=1; //E=1;
DelayNmSec(1); //当系统时钟为1MHz时,延时ns
EN2=0; //E=0;
}
/****************************指令*****************************/
void writecmd2(unsigned char command)
{
unsigned char command_temp;
command_temp = command;
RS2=0; //命令;
DelayNmSec(1);
lcm_w_byte(command_temp);
enable2();
DelayNmSec(1);
RS2=1;
}
/*******************************数据*******************************/
void writedata2(unsigned char Adata)
{
unsigned char data_temp;
data_temp = Adata;
EN2=0;
DelayNmSec(1);
RS2=1;
DelayNmSec(12);
lcm_w_byte(data_temp);
DelayNmSec(1);
enable2();
}
/*****************************位址*****************************/
void setcoordinate2(unsigned char x,unsigned char y)
{
unsigned char address;
if (y == 0) address = 0x80 + x;
else
address = 0xc0 + x;
writecmd2(address);
}
/**************************送字符**************************/
void writestring2(unsigned char X,unsigned char Y,unsigned char s)
{
setcoordinate2(X, Y);
writedata2(s);
}
/******************************************************/
void initLCD2(void)
{
writecmd2(0x38); //4bit 只要改成38就为8位
DelayNmSec(2);
enable2();
DelayNmSec(2);
writecmd2(0x38); //4bit
DelayNmSec(2);
writecmd2(0x0c); //显示开
DelayNmSec(2);
writecmd2(0x01); //显示清屏
DelayNmSec(2);
writecmd2(0x06); //显示光标移动设置
DelayNmSec(2);
}
/************************************************************************
**Tmr1初始化函数
*************************************************************************/
void InitTMR1(void)
{
T1CON=0x0010; // internal Tcy/256 clock
TMR1=0xcf2c;
PR1=0xffff; //16 16*62=1ms
IFS0bits.T1IF=0; //清除TMR1的中断标志
IEC0bits.T1IE=1; //使能中断
}
/*******************计算电压和电流***********************/
void js(void)
{
unsigned char i;
if (Flags.RunMotor)
{
if(Flags.addybit){
for(i=0;i<8;i++)
dynum+=dy;
dynum=dynum>>3;
vmoto=dynum;
if(dynum<372) //低压
{
Flags.RunMotor = 0; // 复位运行标志
Flags.tdybit=1;
} //如果电压小于20V的话继电器关
dynum=0;
Flags.addybit=0;
}
if(Flags.adglbit){
for(i=0;i<8;i++)
glnum+=gl;
glnum=glnum>>3;
if(glnum>=512) //抗干拢一下明天再试--10=4ms
{
glms--;
if(glms<20) glms=20; //电流到这时转速就慢下来,
}
else
{
glms++;
if(glms>510) glms=510;
}
glnum=0;
test = glms;
PDC1 = glms; // 赋值------------------------这就是处理电流后去给PDC寄存器的值,可是却变成调时基了(39K的载波)
PDC2 = glms; // 并装载所有的三个PWM……
PDC3 = glms;
Flags.adglbit=0;
}
}
}
/********************************************************
**定时器1中断服务
*********************************************************/
//void __attribute__((__interrupt__)) _T1Interrupt(void)
void _ISR _T1Interrupt(void)
{
TMR1=0xcf2c;
IFS0bits.T1IF = 0; //清定时器中断标志
if (Flags.RunMotor) //10ms
{
zsjs++;
if(zsjs>=50)
{
S2=!S2;js();
Flags.xsbit=1;
zsjs=0;
zsmoto=zsbit*60/24*2;
zsbit=0;
}
dzbf++;
if(dzbf>=200) //如果超过两秒就认为堵转
{
dzbf=0;
Flags.dzbit=1;
Flags.RunMotor=0;
}
}
}
/*******************************************************************************************************************/
int main(void)
{
TRISC = 0x9FFF;
TRISD = 0x0000;
TRISF = 0x0000;
LATD = 0xFFFF;
LATF = 0xFFFF;
LATE = 0x0000;
TRISE = 0xFFC0; // 设置为输出PWM 信号
CNEN1 = 0x00E0; // 使能CN5、CN6 和CN7
CNPU1 = 0x00E0; // 使能内部上拉
InitMCPWM();
InitADC10();
initLCD2();
InitTMR1();
INTCON2bits.INT0EP = 1;
IFS0bits.INT0IF = 0;
IEC0bits.INT0IE = 1;
IFS0bits.CNIF = 0; // 清零CNIF
IEC0bits.CNIE = 1; // 允许CN 中断
glms=511;
//Flags.Ysbit = 0;
//Flags.gjbit = 1;
//DelayNmSec(1000);
S2=1; //打开继电器
DelayNmSec(1000); //3s
//DelayNmSec(100); //100ms
// 在PORTB 上读霍尔位置传感器
HallValue = PORTB & 0x0038; // 屏蔽其它位,保留RB3、RB4 和RB5
HallValue = HallValue >> 3; // 右移以获得值1、2……6
OVDCON = StateLoTable[HallValue]; // 装载改写控制寄存器
PWMCON1 = 0x0777; // 使能PWM 输出
T1CONbits.TON = 1;
for(ysmoto=0;ysmoto<510;ysmoto++)
{
DelayNmSec(2);
PDC1 = ysmoto; // 赋值------------------开机慢慢启动,这是行的,可以改变速度。
PDC2 = PDC1; // 并装载所有的三个PWM……
PDC3 = PDC1;
}
ysmoto=0x0000;
Flags.RunMotor = 1; // 将标志置1
/*****************/
while(1)
{
DelayNmSec(100);
while (Flags.RunMotor) // 当电机运行时
{
//if(Flags.xsbit){
//Flags.xsbit=0;
test=vmoto;
//vmoto=vmoto*550/1024;
writestring2(0,0,ZF[0]);
writestring2(1,0,ZF[1]);
writedata2((vmoto*550/1024/100)%10+48);
writedata2((vmoto*550/1024/10)%10+48);
writestring2(4,0,ZF[2]);
writedata2((vmoto*550/1024%10)+48);
writestring2(6,0,ZF[3]);
writestring2(7,0,ZF[4]);
writestring2(8,0,ZF[5]);
writestring2(9,0,ZF[6]);
writestring2(10,0,ZF[7]);
writestring2(11,0,ZF[7]);
writestring2(12,0,ZF[1]);
writedata2(((vmoto*550/1024*10)/360)%10+48);
writedata2(((vmoto*550/1024*10)/36)%10+48);
writestring2(15,0,ZF[8]);
writestring2(0,1,ZF[9]);
writestring2(1,1,ZF[10]);
writestring2(2,1,ZF[11]);
writestring2(3,1,ZF[1]);
writedata2((zsmoto/1000)%10+48);
writedata2((zsmoto/100)%10+48);
writedata2((zsmoto/10)%10+48);
writedata2(zsmoto%10+48);
//DelayNmSec(200);
//writestring2(8,1,ZF[4]);//AD
//writedata2((test/1000)%10+48);
writedata2((test/100)%10+48);
writedata2((test/10)%10+48);
writedata2(test%10+48);
//writedata2((zsbit/1000)%10+48);
//writedata2((zsbit/100)%10+48);
//writedata2((zsbit/10)%10+48);
//writedata2(zsbit%10+48);*/
//}
}
if((Flags.dlbit==1)||(Flags.dzbit==1))//如果短路堵转
{
PWMCON1 = 0x0700; // 禁止PWM 输出
OVDCON = 0x0000; // 将PWM 改写为低电平
DelayNmSec(1);
//while(l<30)
//{
while(k<1200)//k为展开速度
{
for(j=0;j<25;j++)
{
if(j<=i)
LATE=0xffea;
else
LATE=0xffc0;
}k++;
}i++;k=0;
//if(i>=20) {i=20;l++;}
//}
//这里要显示什么故障
state=0;
while(1){ //死循环
if(Flags.dlbit) state=1;
if(Flags.dzbit) state=2;
//if(Flags.dlbit) state=3;
//if(Flags.dlbit) state=4;
writestring2(13,1,state%10+48);
}
}
}
}
/*******************************************************************
以下代码用于设置ADC 寄存器,该代码可实现下列功能:
1. 1 个通道转换( 本例中,该通道为RB2/AN2)
2. PWM 触发信号启动转换
3. 电位计连接到CH0 和RB2
4. 手动停止采样和启动转换
5. 手动检查转换完成
*********************************************************************/
void InitADC10(void)
{
ADPCFG = 0xFFF8; // 将端口B 的RB0 到RB2 配置为模拟引脚;将其它引脚配置为数字引脚
ADCON1 = 0x0064; // PWM 启动转换
ADCON2 = 0x0208; // 同时采样4 个通道
ADCHS = 0x0002; // 将RB2/AN2 作为CH0 连接到电位计……
// ch1 连接母线电压、Ch2 连接电机, Ch3 连接电位计
ADCON3 = 0x0080; // Tad 来源于内部RC (4uS)
IFS0bits.ADIF = 0;
IEC0bits.ADIE = 1;
ADCON1bits.ADON = 1; // 启动ADC
}
/********************************************************************
InitMCPWM,对PWM 做以下初始化:
1. FPWM = 39000 hz
2. 独立的PWM
3. 使用OVDCON 控制输出
4. 用从电位计读取的ADC 值设置占空比
5. 将ADC 设置为由PWM 特殊触发信号触发
*********************************************************************/
void InitMCPWM(void)
{
PTPER = FCY/FPWM - 1;
PWMCON1 = 0x0700; // 禁止PWM
OVDCON = 0x0000; // 允许使用OVD 控制
PDC1 = 100; // 将PWM1、PWM2 和PWM3 初始化为100
PDC2 = 100;
PDC3 = 100;
//SEVTCMP = PTPER;
PWMCON2 = 0x0F00; // 后分频比设为1:16
PTCON = 0x8000; // 启动PWM
}
//---------------------------------------------------------------------
// 这是普通的1 ms 延迟程序,用于提供1 mS 到65.5 秒的延迟。
// 如果N = 1,则延迟为1 mS ;如果N = 65535,则延迟为65,535 mS。
// 注意FCY 用于计算。
// 请根据上述定义语句做出必要的更改(PLLx4 或PLLx8 等)
// 以计算出正确的FCY。
void DelayNmSec(unsigned int N)
{
unsigned int j;
while(N--)
for(j=0;j < MILLISEC;j++);
}
一周热门 更多>