迅维网

标题: [转帖][单片机教程3]用AVR M8模糊PID恒温控制改造936焊台 [打印本页]

作者: yzz163    时间: 2009-8-14 14:02
标题: [转帖][单片机教程3]用AVR M8模糊PID恒温控制改造936焊台
原文地址:
http://www.ourdev.cn/bbs/bbs_con ... 936&bbs_id=9999

作者:周远峰 153290557

搞了个焊台自己用放大用LM324比较差··不过温度还是可以稳定,用M8 AD10位 pwm10位温度范围为200-500
精确度1度··
因为程序的原因加上要慢慢整定所以每次升温到设定值前15度就开始整定··
有个缺点就是PID控制恒温时的那几度温度升的很慢··
设置部分还没写好
自己的思路是直接用那个可变电阻做设置
AD转换采样可变电阻来设定温度
设置一按键为温度设定
当按了那按键后可变电阻设定温度有效
没按的话不管你怎么转都不能调节温度
那部分写完后整个代码,发出来大家一起参考
发觉问题还很多的。。希望大家多提下意见,小弟初学的
还有如果要改的话可以不改那电路的引一条采样跟控制线出来直接连上M8也可以的

实物图片:


电路图:





原代码(c 程序):
***************程序开始****************
project        :A/D转换数码管显示
chip type      : atmega8               
clock frequency:内部RC(INT) 8MHz
Author         :周远峰               
********************************/
#include "iom8v.h"
#include "macros.h"
#define osccal 0x7d                        
unsigned long adc_rel;              //处理后世界转换结果
unsigned long adc_rl;               //A/D转换结
unsigned int tmp;                   //设置的温度参数                          
unsigned char adc_mux;              //A/D通道
unsigned char led_buff[3]={0,0,0};  //显示缓存
signed int error0;                  //当前偏差
signed int error1;                  //上次偏差
signed int error2;                  //上上次偏差
signed char Kp;                     //比例常数
signed char Ki;                     //积分常数
signed char Kd;                     //微分常数
signed int kk1;                     //当前控制输出
signed int kk2;                     //上次控制输出
#define NB -3
#define NM -2
#define NS -1
#define ZO 0
#define PS 1
#define PM 2
#define PB 3
#pragma data:code     
//设置数据区位程序储存器
const unsigned char seg_table[16]={0x40,0x79,0x24,0x30,0x19,0x90,0x80,0x78,0x00,0x10,0x08,0x81,0x44,0x21,0x04,0x8c};
const unsigned char KP_table[49]={PB,PB,PM,PM,PS,ZO,ZO,PB,PB,PM,PS,PS,ZO,NS,PM,PM,PM,PS,ZO,NS,NS,PM,PM,PS,ZO,NS,NM,NM,PS,PS,ZO,NS,NS,NM,NM,PS,ZO,NS,NM,NM,NM,NB,ZO,ZO,NM,NM,NM,NB,NB};
const unsigned char KI_table[49]={NB,NB,NM,NM,NS,ZO,ZO,NB,NB,NM,NS,NS,ZO,ZO,NB,NM,NS,NS,ZO,PS,PS,NM,NM,NS,ZO,PS,PM,PM,NM,NS,ZO,PS,PS,PM,PB,ZO,ZO,PS,PS,PM,PB,PB,ZO,ZO,PS,PM,PM,PB,PB};
const unsigned char KD_table[49]={PS,NS,NB,NB,NB,NM,NS,PS,NS,NB,NM,NM,NS,ZO,ZO,NS,NM,NM,NS,NS,ZO,ZO,NS,NS,NS,NS,NS,ZO,ZO,ZO,ZO,ZO,ZO,ZO,ZO,PB,NS,PS,PS,PS,PS,PB,PB,PM,PM,PM,PS,PS,PB};
#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|=0xf1;
PORTD&=0x1f;  //关闭LED
}
/***********************************************

中断显示

*************************************************/
#pragma interrupt_handler timer2_ovf_isr:5
void timer2_ovf_isr(void)
{
unsigned char i;
static unsigned k;
SEI();
TCNT2 = 0xB2; //reload counter value
for(i=0;i<3;i++)
   {
   PORTB=led_buff;
   PORTD|=(1<<(i+5));//待显示的位置1
   delay_ms(1);
   PORTD&=0x1f;     //关闭LED
   }
}
/************************************************************

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=3;
Ki=3;
Kd=3;
kk1=0; //当前
kk2=100;
error0=0;
error1=0;
error2=0;
tmp=300;
}
void pidcalc_zizheng(void)
{
if (tmp>adc_rel)
   {
        if (kk2<500) kk2+=3;
        else kk2=500;
   }
if ((tmp-1)>adc_rel)
   {
        if (kk2<500) kk2+=3;
        else kk2=500;
   }
/*if ((tmp-2)>adc_rel)
   {
        if (kk2<600) kk2+=3;
        else kk2=600;
   }*/
if (tmp<adc_rel)
   {
   if (kk2>10) kk2-=3;
   else kk2=10;
   }
if ((tmp+1)<adc_rel)
   {
   if (kk2>10) kk2-=3;
   else kk2=10;
   }
/*if ((tmp+2)<adc_rel)
   {
   if (kk2>10) kk2-=3;
   else kk2=10;
   }
/*if (tmp>adc_rel)
   {
   if (Ki<200) Ki+=4;
   else Ki=400;
   }
if (tmp<adc_rel)
   {
   if (Ki>0) Ki-=4;
   else Ki=0;
   }  */
}
/*******************************************************

PID函数

********************************************************/
void pidcalc(void)
{
signed long KPP;
signed long KII;
signed long KDD;
signed int i;
signed char j;
error0=tmp-adc_rel;
j=error0-error1;
i=7*(3+error0)+(3+j);
if(i<49)
    {
     if (i>0)
            {
                Kp=KP_table;
                Ki=KI_table;
                Kd=KD_table;
                }
     }                        //输出                     
if ((tmp-15)<adc_rel)                         //比设定低一定值时开始PID
  {
   if((tmp+5)>adc_rel)                        //比设定高时关PID
       {
       KPP=Kp*(error0-error1);                //比例
       KII=error0*Ki;                         //积分
       KDD=Kd*(error0-(2*error1)+error2);     //微分
       kk1=(KPP+KII+KDD)*4+kk2;  
       if(kk1<0x3ef)
                   {
                           if (kk1>=10) OCR1A=kk1;   
                           else OCR1A=0;               
               }
           else
                   {   
                    OCR1A=0x3ff;
               }      
                           /*if((tmp-2)>adc_rel)
                                {
                                if(OCR1A<500) OCR1A+=20;
                                else OCR1A=500;
                                        }*/
           }
    else
           {
           //if (tmp<adc_rel)
                 // {
                  ///  if (OCR1A>50) OCR1A-=20;
                  //  else OCR1A=50;
           OCR1A=0;  
                 // }
       }
   }
else
   {
    OCR1A=0x3ff;
   }
error2=error1;
error1=error0;  
}
/***********************************************

ADC初始化

************************************************/
void adc_init(void)  
{
DDRC=0x00;
PORTC=0X00;
ADCSRA=0X00;
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<2048)
    i++;
else  
     {
         adc_rel=adc_rl/2048;
         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因为不处理小数实际就等于右移了
   }
   
}
/***************************************************************

主程序

***************************************************************/
void main(void)
{
unsigned char i;
unsigned int  k;
unsigned int adc_old;
unsigned long adc_ol;
unsigned int adc_o;
DDRD=0xff;
PORTD=0xf0;
OSCCAL=osccal;
TIMSK = 0x40; //timer interrupt sources
adc_mux=0;
adc_init();
timer1_init();
pidcalc_init();
SEI();
for(i=0;i<3;i++)
   led_buff=seg_table[8];
for(i=0;i<200;i++)
   timer2_init();
   adc_old=0;
   adc_rel=0;
  while(1)
     {
                if(adc_old!=adc_rel)//ADC更新完毕就执行数据处理
                 {
                      adc_old=adc_rel;
                          pidcalc();
                          pidcalc_zizheng();
                            if(k<5)
                                    {
                                        k++;
                                        adc_ol+=adc_old;
                                        }
                                else
                                    {
                                        adc_o=adc_ol/5;
                                        adc_ol=0;
                                        k=0;
                                        }        
                          }  
            ADCtoBCD(adc_o);  
         }
}
***************程序结束****************
作者: yzz163    时间: 2009-8-14 22:11
AVR M8中文手册: ATmega8_cn.pdf (2.45 MB, 下载次数: 131)