本帖最后由 TT的爱 于 2013-3-6 14:58 编辑
无论怎么调,速度总是差那么一点。请各位高手帮我看一下。
我使用的芯片是PIC16F876A,编译环境是CCS。目标是通过PID实现直流有刷电机调速。
RA0接口为模拟电压输入,AD转换完的数据由单片机处理后,可以设定目标转速。光电编码器通过RC0接口输入计数脉冲,计算出实际转速,通过PID计算不断调整,最终接近目标转速。
定时器0每过10ms取计数器1的脉冲计数值,光电编码器分辨率为360。现在有两个情况比较奇怪,
1:我把P参数从1到10,I参数从0到5都试了一遍。当I参数过大时会出现实际速度变低的情况,当I参数为2时电机不能转动,其他情况下实际速度基本区别不大。
2:我将目标速度调高时(例如超过5000r/min),则实际速度比目标速度高出的部分会越来越大。而当目标速度调低时(如低于3000r/min),实际速度比目标速度低出的部分会越来越大。无论P,I如何设定,都是这样的情况。
程序如下。
#include <16f876a.h>
#include <lcd.h>
#fuses HS,PUT,NOPROTECT,NOWDT
#use delay(CLOCK = 20000000,RESTART_WDT) //时钟频率20MHz
#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
unsigned long int overflow=0,get_count,new_count=0,get_speed=0,new_error=0,new_speed=0,old_count=0,old_speed=0,old_error=0;//所需变量
unsigned int ch0_channel,new_duty;
float new_pwm,set_speed;
#int_TIMER1
TIMER1_isr() //定时器1中断函数
{
overflow++;
}
#int_TIMER0
TIMER0_isr() //定时器0中断函数
{
set_timer0(0x3C); //定时器0每过10ms发生一次中断
new_count = get_timer1(); //读取计数器1的脉冲计数值
get_count = new_count-old_count; //PID部分
get_speed = get_count*50/3;
new_error = set_speed-get_speed;
new_speed = (new_error-old_error)*1+1*new_error+get_speed;
new_pwm = (float)new_speed/31; //感觉是这边出问题了
new_duty = (int)new_pwm;
set_pwm1_duty(new_duty);
old_error = new_error;
old_count = new_count;
old_speed = new_speed;
overflow = 0;
}
void main(void)
{
set_tris_a(0b11111111); //0:输出、1:输入
set_tris_b(0b11000000); //0:输出、1:输入
set_tris_c(0b00000001); //0:输出、1:输入
SETUP_ADC_PORTS(RA0_ANALOG); //RA0 为模拟输入
SETUP_ADC(ADC_CLOCK_DIV_32); //Fosc/32
set_adc_channel(0); //选择channel0
enable_interrupts(GLOBAL); //允许全局中断
enable_interrupts(INT_TIMER0); //允许定时器0中断
enable_interrupts(INT_TIMER1); //允许定时器1中断
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256); //定时器0分频
setup_timer_1(T1_EXTERNAL_SYNC|T1_DIV_BY_1); //定时器1用作计数器
set_timer0(0x3C);
setup_timer_2(T2_DIV_BY_16,255,1); //定时器2设置
setup_ccp1(ccp_pwm); // 选择PWM模式
read_adc(ADC_START_ONLY); //读取八位AD值
lcd_initial(); //lcd初始化
while(1)
{
ch0_channel = read_adc(ADC_READ_ONLY); //读取AD转换值
read_adc(ADC_START_ONLY); //AD转换开始
set_speed = (float)31*ch0_channel; //经过测试测试,速度大概是channel0数值的31倍
printf(lcd_data,"sv:%3.3f",set_speed); //第一行显示目标速度
lcd_cmd(0b11000000);
printf(lcd_data,"pv:%lu",get_speed); //第2行显示实际速度
delay_ms(200); //lcd延迟200ms
lcd_clear(); //lcd清除
}
}
|