×
单片机 > 单片机程序设计 > 详情

基于AVR的PID温度控制系统原程序

发布时间:2020-08-21 发布时间:
|
/*******************************************************************************

Clock F  : 8M

*******************************************************************************/
#include
#include
//#include "LCD1602.h"
//#include "port_init.h"
//#include "display.h"
//#include "temp_set.h"
//#pragma interrupt_handler SET:6;

#define uchar unsigned char
#define uint unsigned int
#define RS_CLR PORTG &= ~(1 << PG0)
#define RS_SET PORTG |= (1 << PG0)

#define RW_CLR PORTG &= ~(1 << PG1)
#define RW_SET PORTG |= (1 << PG1)

#define EN_CLR PORTG &= ~(1 << PG2)
#define EN_SET PORTG |= (1 << PG2)
#pragma interrupt_handler TC3:30

float  target;
int temperature;
unsigned char zhi[3]={0,0,0},flag,f,t;
unsigned char *p="0123456789.";
unsigned char Z;
unsigned char a,b,c,k;
float gap;
unsigned int  adc_data0;

void port_init()
{
DDRA=0xff;//A口为输出端口
PORTA=0x00;//不带上拉电阻
DDRB=0xff;//B口为输出口
DDRD=0xff;//带上拉电阻
PORTD=0x03;//D口为输入口
DDRF=0x00;//为输入口
PORTF=0x00;//不带上拉电阻
DDRG=0xff;//G口为输出口
PORTG=0x00;//不带上拉电阻
DDRC=0X00;
PORTC=0X00;
DDRE=0x00;
PORTE=0x00;
}

 

 

//us延时函数
void delay_us(uint n)   //8*0.125=1us
{
    int i,j;
 for(j=0;j<8;j++)
 {
   for (i=0;i    NOP();
 }  
}

//ms延时函数
void delay_ms(uint i)
{
    while(i--)
    {                          
     uint j;                
        for(j=1;j<=1332;j++)   
      ;               
    } 
}


//显示屏初始化函数
void LCD_init(void) 
{
 DDRA = 0xFF;          //I/O口方向设置
 DDRD|=(1<  delay_ms(15);                           //上电延时一段时间,使供电稳定
 Write_Instruction(0x38);    //8bit interface,2line,5*7dots
 delay_ms(5);
 Write_Instruction(0x38); 
 delay_ms(5);
 Write_Instruction(0x38); 

 Write_Instruction(0x08); //关显示,不显光标,光标不闪烁
 Write_Instruction(0x01); //清屏
 delay_ms(5);
 
 Write_Instruction(0x04); //写一字符,整屏显示不移动
 //Write_Instruction(0x05); //写一字符,整屏右移
 //Write_Instruction(0x06); //写一字符,整屏显示不移动
 //Write_Instruction(0x07); //写一字符,整屏左移
 delay_ms(5);
 
 //Write_Instruction(0x0B); //关闭显示(不显示字符,只有背光亮)
 Write_Instruction(0x0C); //开显示,光标、闪烁都关闭
 //Write_Instruction(0x0D); //开显示,不显示光标,但光标闪烁
 //Write_Instruction(0x0E); //开显示,显示光标,但光标不闪烁
 //Write_Instruction(0x0F); //开显示,光标、闪烁均显示
}

//控制LCD写时序
void LCD_en_write(void)        //EN端产生一个高电平脉冲,控制LCD写时序
  {
    EN_SET;
    delay_us(3);
    EN_CLR;
 delay_us(3);
  }
  
//清屏函数
void LCD_clear()
{
  Write_Instruction(0x01);
  delay_ms(5);
}
  
//写指令函数
void Write_Instruction(uchar command)
{
  RS_CLR;
  RW_CLR;
  EN_SET;
  PORTA=command;
  LCD_en_write();//写入指令数据
}

//写数据函数
void Write_Data(uchar Wdata)
{
  RS_SET;
  RW_CLR;
  EN_SET;
  PORTA=Wdata;
  LCD_en_write();//写入数据
}

//字符显示初始地址设置
void LCD_SET_XY(uchar X,uchar Y)
{
  uchar address;
  if(Y==0)
    address=0x80+X;//Y=0,表示在第一行显示,地址基数为0x80
  else 
    address=0xc0+X;//Y非0时,表时在第二行显示,地址基数为0XC0
  Write_Instruction(address);//写指令,设置显示初始地址
}

//在第X行Y列开始显示,指针*S所指向的字符串
void LCD_write_str(uchar X,uchar Y,uchar *s)
{
  LCD_SET_XY(X,Y);//设置初始字符显示地址
  while(*s)//逐次写入显示字符,直到最后一个字符"/0"
  {
    Write_Data(*s);//写入当前字符并显示
 s++;//地址指针加1,指向下一个待写字符
  }
}

//在第X行Y列开始显示Wdata所对应的单个字符
void LCD_write_char(uchar X,uchar Y,uchar Wdata)
{
  LCD_SET_XY(X,Y);//写地址
  Write_Data(Wdata);//写入当前字符并显示
  
 
}

///////////////////////i2c通信子程序///////////////////////
///////////////////////////////////////////////////////////
//初始化
void i2c_init()
{
    PORTD&=~0x80;
    TWBR=0x01 ;  //速率=16M/(16+2*16)=33.3k
    TWSR=0x01 ;  //分频因子=4
}


//开始信号
void i2c_start()
{
    TWCR=0xa4 ;
    while(!(TWCR&0x80)); 
}


//写数据
void i2c_wrdata(uchar i2c_data)
{
    TWDR=(i2c_data);
    TWCR=0x84 ;
    while(!(TWCR&0x80));
}


//读数据noack
uchar i2c_redata()
{
    uchar i2c_data ;
    TWCR=0x84 ;
    while(!(TWCR&0x80));
 //delay_ms(20);
    i2c_data=TWDR ;
    return(i2c_data);
}

//读数据ack
uchar i2c_redataack()
{
    uchar i2c_data ;
    TWCR=0xc4 ;
    while(!(TWCR&0x80));
    i2c_data=TWDR ;
    return(i2c_data);
}

 

//停止信号
void i2c_stop()
{
    TWCR=0x94 ;
}

//i2c的写字节子程序
void i2c_wrbyte(uchar i2c_data,uchar i2c_add)
{
    do 
    {
        i2c_start();
        i2c_wrdata(0xa0);
    }
    while((TWSR&0xf8)!=0x18);     //判断总线是否可以操作
    
    i2c_wrdata(i2c_add);
    i2c_wrdata(i2c_data);
    i2c_stop();
}

//i2c的页写子程序
//参数1:要写入的数组指针
//参数2:写入的首地址
//参数3:数组的单元数
void i2c_wrpage(uchar *i2c_data,uchar i2c_add,uchar i2c_nub)
{
    uchar i;
    do 
    {
        i2c_start();
        i2c_wrdata(0xa0);
    }
    while((TWSR&0xf8)!=0x18);
    //判断总线是否可以操作
    i2c_wrdata(i2c_add);
    for(i=0;i     {
        i2c_wrdata(*i2c_data++);
    }
    i2c_stop();
}

//i2c读一个字节子程序
uchar i2c_rebyte(uchar i2c_add)
{
    uchar i2c_data ;
    do 
    {
        i2c_start();
        i2c_wrdata(0xa0);
    }
    while((TWSR&0xf8)!=0x18);
    //判断总线是否可以操作
    i2c_wrdata(i2c_add);
    i2c_start();
    i2c_wrdata(0xa1);
    i2c_data=i2c_redata();
    i2c_stop();
    return(i2c_data);
}


//i2c连续读子程序
//参数1:16位地址
//参数2:读取字节数  注:<=64
//读出的数据只能从全局数组变量获得
void i2c_rese(uchar *i2c_data,uchar i2c_add,uchar i2c_nub)
{
    uchar i;
    do 
    {
        i2c_start();
        i2c_wrdata(0xa0);
    }
    while((TWSR&0xf8)!=0x18);
    //判断总线是否可以操作
    i2c_wrdata(i2c_add);
    i2c_start();
    i2c_wrdata(0xa1);
    for(i=0;i     {
        *i2c_data=i2c_redataack();
  i2c_data++;
    }
    *i2c_data=i2c_redata();                 //最后一个字节发出no ack信号
    i2c_stop();
}

 

 


/************************************
用    途:矩阵键盘扫描子程序
Taget   :mega128
crystal :8M
介    绍:用的是C口
入口参数:
出口参数:没有是16不然是0-15
*************************************/
void delay(uint ms)
{
        uint i,j;
 for(i=0;i     {
    for(j=0;j<1141;j++);
       }
}


char key_vetrix()
{
  uchar key,i=0X7F,j;
     delay(10);
  if(key_press())
  {
     do
  {
   i=(i<<1|i>>7);
   PORTC=i;
   DDRC=0X0F;
   
   key=PINC;
   j=key&0XF0;
   
     }while(j==0XF0);
  
  while(key_press());
  switch(key)
  {
       case 0xEE:
      key=0x0;
   break;
    case 0xDE:
      key=0x1;
   break; 
    case 0xBE:
      key=0x2;
   break;
    case 0x7E:
      key=0x3;
   break;
    case 0xED:
      key=0x4;
   break;
    case 0xDD:
      key=0x5;
   break;
    case 0xBD:
      key=0x6;
   break;
    case 0x7D:
      key=0x7;
   break;
    case 0xEB:
      key=0x8;
   break;
    case 0xDB:
      key=0x9;
   break;
    case 0xBB:
      key=0xA;
   break;
    case 0x7B:
      key=0xB;
   break;
    case 0xE7:
      key=0xC;
   break;
    case 0xD7:
      key=0xD;
   break;
    case 0xB7:
      key=0xE;
   break;
    case 0x77:
      key=0xF;
   break;
    default:
         key=16;  
  }
  }
  else
  {
     key=16;
  }
  return key;
}
uchar key_press()
{
     uchar i;
     DDRC=0XFF;
  PORTC=0XF0;
  
  DDRC=0X0F;
  
  i=PINC;
  if(i==0XF0)
  {
    DDRC=0XFF;
    return 0;
  }
  else
  {
    DDRC=0XFF;
    return 1;
  }
}
//********************************


 float ADC0()
{  unsigned char adc_l0,adc_h0;
   unsigned int qianwei0,baiwei0,shiwei0,gewei0;
  
   float temp;
   ADCSRA=0x00;
   ADMUX=0x40;//01000000在
   ADCSRA=(1<    //delay_ms(5);  
   while(!((ADCSRA)&(BIT(ADIF))));                            //给出转换的时间
   adc_l0=ADCL;
   adc_h0=ADCH;
   adc_data0=adc_h0<<8|adc_l0;           //存放的是转换后的数字量
   temp=adc_data0;
   temp=temp/4;
   
   adc_data0=20.0+((temp)*1.0-28)*1.0;
 if(adc_data0>40.0&&adc_data0<=50.0)
   {
  adc_data0=41.0+((temp)*1.0-62)*0.5;
   }
 else if(adc_data0>50.0)
   {
  adc_data0=50.0+((temp)*1.0-72)/3.2;
   }
  
   if(t==1)
   {
   gewei0   = (int) adc_data0%10; 
   qianwei0 = (int) adc_data0/1000;
   baiwei0  = (int) adc_data0%1000/100;
   shiwei0  = (int) adc_data0%100/10;


  //LCD_write_char(10,1,p[qianwei0]);
  LCD_write_char(11,1,p[baiwei0]); 
  LCD_write_char(12,1,p[shiwei0]); 
  LCD_write_char(13,1,p[gewei0]);
  t=0;
  }
  return  adc_data0 ;//adc_data0是从数模转换过来的当前的温度数值,是一个十位的二进制值
   
}

 


void PID()
{
unsigned int a;
unsigned int wanwei0,qianwei0,baiwei0,shiwei0,gewei0;

 

  float kp=1.0,ki=0.9,kd=2.0;
  float piancha1,piancha2,piancha3,leiji;
  piancha3=target-ADC0();//target 是一个目标温度,是一个全局变量
  temperature=leiji+(kp+ki+kd)*piancha3+kd*piancha1-(kp+2*kd)*piancha2;
  piancha1=piancha2;
  piancha2=piancha3;
  leiji=temperature;

 if(f==1)
   {
   if(temperature<0)
   {
   // LCD_write_char(1,1,);
   temperature=0-temperature;
   }
   else
   //LCD_write_char(1,1,);
   wanwei0  =  temperature/10000;
   qianwei0 =  temperature%10000/1000;
   baiwei0  =  temperature%1000/100;
   shiwei0  =  temperature%100/10;
   gewei0   =  temperature%10;
  
  LCD_write_char(20,1,p[qianwei0]);
  LCD_write_char(21,1,p[qianwei0]);
  LCD_write_char(22,1,p[baiwei0]); 
  LCD_write_char(23,1,p[shiwei0]); 
  LCD_write_char(24,1,p[gewei0]);
  f=0;
  }
  OCR1B=1250-20* temperature;
 /* gap=(target-ADC0())/target*100;
 /* if(gap<0)
  OCR1B=1249;
  else if(gap=0)
  OCR1B=1200;
  else if(gap>0&&gap<=2)
  OCR1B=500;
  else if(gap>2)
   OCR1B=10;
  /*if (gap>0)
  {  
    if(gap>80)
    OCR1B=100;
 if(gap<=80&&gap>=70)
 OCR1B=200;
 if(gap<=70&&gap>=60)
 OCR1B=300;
 if(gap<=60&&gap>=50)
 OCR1B=400;
 if(gap<=50&&gap>=40)
 OCR1B=500;
 if(gap<=40&&gap>=30)
 OCR1B=600;
 if(gap<=30&&gap>=20)
 OCR1B=700;
 if(gap<=20&&gap>=10)
 OCR1B=900;
 if(gap<=10&&gap>=0)
 OCR1B=500;
  }*/
  /*if(gap<=0)
  OCR1B=1249;
  if(gap>0)
  OCR1B=12;*/
  //return a;
 
}

 

void  setxianshi()
{    a=(int)target/100;
  b=(int)target%100/10;
  c=(int)target%10;
  LCD_write_char(11,0,0x30+a);
     LCD_write_char(12,0,0x30+b);
     LCD_write_char(13,0,0x30+c);
}
void  SET()
  {
  
  //ETIMSK&=~BIT(2);//关定时中断
    //if((Z=key_vetrix())!=16)
 //while(1)
   //{ 
       //setxianshi();
    Z= key_vetrix();
        if (Z==12) 
   { char t=1;
       while(t)
      {Z=key_vetrix();
       setxianshi();
       LCD_write_str(0,0," SET TEMP:");
       if(Z==14)
        {
        target++;
        //Z=16;
        }
        if(Z==15)
        {
        target--;
        //Z=16;
       }
       if(0<=Z&&Z<=9)
       {            if(k==0)
          target=(int)target%100+Z*100;
            if(k==1)
             target=(int)target-(int)target/10%10*10+Z*10;
            if(k==2)
          target=(int)target-(int)target%10+Z; 
            k++;
         if(k==3)
         k=0;
          }
       if(Z==13)
       t=0;
      }
      }
     LCD_write_str(0,0,"TARGET IS:");
  // }
  }

void kaijixianshi()
{ char i;
  LCD_write_str(0,0," WELCOME TO THE ");
  LCD_write_str(0,1,"     DEVICE     ");
  PORTB&=~BIT(0);
  PORTB|=BIT(1);
  /*for(i=0;i<20 ;i++ )
  { 
   Write_Instruction(0x18);
   delay_ms(100);
  }*/
  
  delay_ms(3000);
  PORTB|=BIT(0);
  PORTB&=~BIT(1);
}

void  main ()
{
int num1;
port_init();
LCD_init(); //LCD初始化
i2c_init();  //初始化
target=i2c_rebyte(0x05);  //读出地址0x12的字节
LCD_clear();//清屏  
kaijixianshi();
LCD_clear();//清屏  
//LCD_write_str(0,1,"TEMP IS:      .C");
EICRB=0x00;
TCCR1A=0x63;
TCCR1B=0x13;
OCR1A=1249;
OCR1B=31;

/**** 定时器3的普通模式*******/
TCCR3A=0X00;
TCCR3B=0X05;
TCNT3H=0X85;
TCNT3L=0XE0;
ETIMSK|=BIT(2);
SREG|=BIT(7);//2秒钟发生一次中断

 a=(unsigned char)target/100;
 b=(unsigned char)target%100/10;
 c=(unsigned char)target%10;
 LCD_write_str(0,0,"TARGET IS:    .C");
 LCD_write_str(0,1," TEMP  IS:    .C");
 LCD_write_char(11,0,p[a]);
 LCD_write_char(12,0,p[b]);
 LCD_write_char(13,0,p[c]);
while(1)
     {
  /*target=a*100+b*10+c*1;
  if((target-temperature)>=0)
      {
  // LCD_write_char(11,0,p[a]);
         //LCD_write_char(12,0,p[b]);
         //LCD_write_char(13,0,p[c]);
    SET();
    num1=PID();
    if(num1>40)
    OCR1B=100;
    else 
    num1=PID();
    if(num1>30&&num1<40)
    OCR1B=200;
    else 
    num1=PID();
    if (num1>20&&num1<30)
    OCR1B=300;
    else
    num1=PID();
    if(num1>10&&num1<20)
    OCR1B=500;
    else 
    num1=PID();
    if(num1>0&&num1<10)
    OCR1B=700;
    else
    num1=PID();
    OCR1B=1000;
   }
      }*/
   SET();
   PID();
   i2c_wrbyte((unsigned char)target,0x05);  //字节写入
}

}

 void TC3()
 {
 t=1;
 f=1;
 TCNT3H=0X85;
 TCNT3L=0XE0;
 }

 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
8051单片机int字节的实现