|
楼主 |
发表于 2008-6-13 19:14:19
|
显示全部楼层
上次那个很多缺陷·
改用普通PID吧!效果比以前好点点,
/*******************************
project :A/D转换数码管显示
chip type : atmega8
clock frequency:内部RC(INT) 8MHz
Author :周远峰
********************************/
#include "iom8v.h"
#include "macros.h"
#define osccal 0x7d
unsigned int adc_rel; //处理后世界转换结果
unsigned long adc_rl; //A/D转换结
unsigned int tmp; //设置的温度参数
unsigned char adc_mux; //A/D通道
unsigned int ww; //闪时间
unsigned int kk;
unsigned char k1; //显示
unsigned int k11;
unsigned char key1; //按键标志
unsigned char led_buff[3]={0,0,0}; //显示缓存
signed int error0; //当前偏差
signed int error1; //上次偏差
signed int error2; //上上次偏差
signed int Kp; //比例常数
signed int Ki; //积分常数
signed int Kd; //微分常数
signed int kk1; //当前控制输出
signed int kk2; //上次控制输出
#pragma data:code
//设置数据区位程序储存器
const unsigned char seg_table[17]={0x40,0x79,0x24,0x30,0x19,0x90,0x80,0x78,0x00,0x10,0x08,0x81,0x44,0x21,0x04,0x8c,0xff};
#pragma data:data
//设置数据区回到数据储存器
/*********************************************************
延时函数
*********************************************************/
void delay_us(int time) //微秒级延时
{
do
time--;
while (time>1);
}
void delay_ms(unsigned int time) //毫秒级延时
{
while (time!=0)
{
delay_us(1000);
time--;
}
}
/************************************************************
中断显示初始化
TIMER2 initialize - prescale:1024
WGM: Normal
desired value: 10mSec
actual value: 9.984mSec (0.2%)
*************************************************************/
void timer2_init(void)
{
TCCR2 = 0x00; //stop
ASSR = 0x00; //set async mode
TCNT2 = 0xB2; //setup
OCR2 = 0x4E;
TCCR2 = 0x07; //start
DDRB = 0xff; //PC口为推挽1输出
PORTB = 0xff; //PC口内部上拉
DDRD |= 0xf0;
PORTD &= 0x1f; //关闭LED
}
/***********************************************
中断显示
*************************************************/
#pragma interrupt_handler timer2_ovf_isr:5
void timer2_ovf_isr(void)
{
unsigned char i;
TCNT2 = 0xB2; //reload counter value
if (k1==0)
{
for(i=0;i<3;i++)
{
PORTB = led_buff;
PORTD |= (1<<(i+5));//待显示的位置1
delay_ms(1);
PORTD &= 0x1f; //关闭LED
}
}
}
void shanshuo(void)
{
if(k11<ww)
{
k11++;
k1 = 0;
}
else
{
if (kk<ww)
{
kk++;
k1 = 1;
}
else
{
k11 = 0;
kk = 0;
}
}
}
//Watchdog initialize
// prescale: 2048K
void watchdog_init(void)
{
WDR (); //this prevents a timeout on enabling
WDTCR |= (1<<WDCE) | (1<<WDE);/* 30-Oct-2006 Umesh*/
WDTCR = 0x0F; //WATCHDOG ENABLED - dont forget to issue WDRs
}
/************************************************************
PWM初始化,OC1A口输出
*************************************************************/
void timer1_init(void)
{
TCCR1B = 0x00; //stop
TCNT1H = 0x00; //setup
OCR1A = 200;
TCCR1A = (1<<WGM11)|(1<<WGM10)|(1<<COM1A1);//输出低电平
TCCR1B = (1<<CS11)|(1<<CS10); //
}
/*****************************************************
PID初始化
******************************************************/
void pidcalc_init(void)
{
Kp = 45;
Ki = 3;
Kd = 1;
kk1 = 0; //当前
kk2 = 0;
error0 = 0;
error1 = 0;
error2 = 0;
}
/*******************************************************
PID函数
********************************************************/
void pidcalc(void)
{
signed long KPP;
signed long KII;
signed long KDD;
signed int i;
signed char j;
if (key1==0) //设置温度时PID不工作
{
error0=tmp-adc_rel;
if (100<adc_rel) //比设定低一定值时开始PID
{
if((tmp+20)>adc_rel)
{
KPP =Kp*(error0-error1);
KII =Ki*error0;
KDD = Kd*(error0-(2*error1)+error2);
kk1 =KPP+KII+KDD+kk2;
if (kk1<0x3ef)
{
if (kk1>0) OCR1A = kk1;
else { OCR1A = 0; }
}
else {OCR1A = 0x3ff;}
}
else {OCR1A = 0;}
}
else {OCR1A = 0x3ff;}
kk2=kk1;
error2 = error1;
error1 = error0;
}
else {}
}
/***********************************************
ADC初始化
************************************************/
void adc_init(void)
{
DDRC = 0;
PORTC = 0;
ADCSRA = 0;
ADMUX = (1<<REFS1)|(1<<REFS0)|(adc_mux&0x0f); //选择内部2.56V为基准AREF外加滤波电容
ACSR = (1<<ACD); //关闭模拟比较器
ADCSRA = (1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//128分频
}
/*********************************************
ADC中断处理函数
**********************************************/
#pragma interrupt_handler adc_isr:15
void adc_isr(void)
{
static unsigned i;
adc_rl+=ADC&0x3ff;
ADMUX = (1<<REFS1)|(1<<REFS0)|(adc_mux&0x0f); //选择内部2.56V位基准
ADCSRA |= (1<<ADSC); //启动AD转换
if (i<1023)
i++;
else
{
adc_rel = adc_rl/1024;
adc_rel = adc_rel*3/5;
i=0;
adc_rl = 0;
}
}
/******************************************************
ADC数据转压缩BCD
******************************************************/
void ADCtoBCD(unsigned int temp)
{
unsigned char i;
for (i=0;i<3;i++)
{
led_buff=seg_table[temp%10];/*temp%10求余数‘假设一个数是234那么234/10=23余4也就是查找4的段码*/
temp=temp/10;// 234/10=23因为不处理小数实际就等于右移了
}
}
/**************************************************************
温度设置
**************************************************************/
#pragma interrupt_handler int0_isr:2
void int0_isr(void)
{
unsigned char i;
delay_ms(500);//external interupt on INT0
i=PIND&0x04;
if(i==0x04)
return;
else
{
if(key1==1)
{
tmp=adc_rel;
EEPROM_conwrite ();
adc_mux = 0;
key1 = 0;
k1 = 0;
ww = 0;
}
else
{
ww=600;
adc_mux = 1;
key1 = 1;
OCR1A = 0;
}
}
}
/********************************************************
子程序名: EEPROM_write
功 能: 写一字节数据到片内指定地址单元
输入参数: /
输出参数: /
描 述: /
********************************************************/
void EEPROM_write(unsigned int uAddr,unsigned char uData)
{
EEAR = uAddr; //写操作的16位地址寄存器
EEDR = uData; //写数据到数据寄存器
EECR |= (1<<EEMWE); //允许EEPROM操作
EECR |= (1<<EEWE); //开始写操作
while (EECR & (1<<EEWE)) //等待写操作完成
;
}
/********************************************************
子程序名: EEPROM_read
功 能: 从片内指定地址单元读一字节数据
输入参数: /
输出参数: /
描 述: /
********************************************************/
unsigned char EEPROM_read(unsigned int uAddr)
{
while (EECR & (1<<EEWE)) //等待前一次写操作完成
;
EEAR = uAddr; //写操作的16位地址寄存器
EECR |= (1<<EERE); //开始读操作
return EEDR; //输出读出的数据
}
/********************************************************
子程序名: EEPROM_conwrite
功 能: 写
输入参数: /
输出参数: /
描 述: /
********************************************************/
void EEPROM_conwrite (void)
{
unsigned char j,k;
unsigned int i;
i = tmp;
j = (unsigned char)i;
k = (unsigned char)(i>>8);
EEPROM_write (10,j);
EEPROM_write (11,k);
}
/********************************************************
子程序名: EEPROM_conread
功 能: 写
输入参数: /
输出参数: /
描 述: /
********************************************************/
void EEPROM_conread(void)
{
unsigned char j,k;
unsigned int i;
j = EEPROM_read (10);
k = EEPROM_read (11);
i = (unsigned int)j;
i |= (unsigned int)(k<<8);
tmp= i;
}
/********************************************************
子程序名: init_init
功 能: 初始化
输入参数: /
输出参数: /
描 述: /
********************************************************/
void init_init(void)
{
DDRD=0xff;
PORTD = 0xf0;
k1 = 0;
kk = 0;
adc_rel = 0;
OSCCAL = osccal;
adc_mux = 0;
tmp = 0;
TIMSK = 0x40; //timer interrupt sources
MCUCR = (1<<ISC01)|(1<<ISC00);
GICR = (1<<INT0);
}
/***************************************************************
主程序
***************************************************************/
void main(void)
{
unsigned int adc_old;
init_init();
EEPROM_conread();
watchdog_init();
adc_init();
timer1_init();
timer2_init();
pidcalc_init();
SEI();
adc_old=0;
while(1)
{
if(adc_old!=adc_rel)//ADC更新完毕就执行数据处理
{
adc_old=adc_rel;
pidcalc();
}
WDR ();
shanshuo();
ADCtoBCD(adc_old);
}
} |
|