找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1304|回复: 4
打印 上一主题 下一主题
收起左侧

基于18b20的PID温控单片机程序

[复制链接]
跳转到指定楼层
楼主
Tautee 发表于 2019-7-21 19:01 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
主控芯片采用STM32F103RCT6,用18b20作为温度传感器,可以通过串口发送命令和当前温度等信息。PI调节,使用PWM控制固态继电器继而控制加热器通断。
/******************************************
/*********    无线遥控温度控制            
/*********    MCU:STC89C52      
* *******    温度传感器:18b20     
* 控制方法:PID控制
* 串口命令格式'\0x04'+命令+'\0x05'
*****************************************/

#include <reg51.h>
#include <stdio.h>

#define uchar unsigned char
#define uint  unsigned int

#define FOSC 11059200L      //晶振频率
#define BAUD 9600           //串口波特率

#define PWMPERIOD 100               //周期,*5ms
#define OPEN 1
#define CLOSE 0

sbit pwm_out=P1^2;              //pwm输出口
sbit dq = P0^1;             //18b20数据接口
sbit die=P1^0;

uchar tx[10];               //温度结果储存,0、1为整数位,其余小数
uchar temp_now;
//float temp_now_dec;
float temp_set;
uchar cmd;
uchar time_length;
uchar duty_ratio;
//float duty;
float kp,ki,kd;             //pid参数
float e_k,e_k_1,e_k_2;      //偏差,此刻,上一刻,上上一刻

uchar cmd_flag;             //状态标志量,与接收命令有关
uchar run;                      //运行状态,为1则启动温控

//微秒级延时
void DelayUs(int num)
{
    while(num--) ;
}

//毫秒级延时
void DelayMs(uint di) //延时
{
    uint da,db;
    for(da=0;da<di;da++)
        for(db=0;db<100;db++);
}

//发送
void Print(uchar str)
{
    //TR0=0;
    SBUF=str;
    while(!TI);
    TI=0;
    //TR0=1;
}

//PID计算程序
void CalculatePID()
{
    char u_add;
   
    e_k_2=e_k_1;
    e_k_1=e_k;
    e_k=temp_set-temp_now;
    //u_add=(kp+ki+kd)*e_k-(kp+2*kd)*e_k_1+kd*e_k_2;
    u_add=kp*e_k-kp*e_k_1;
//  duty=duty-u_add;
//  if(duty>1)
//  {
//      duty=1;
//  }else if(duty<0)
//  {
//      duty=0;
//  }
//  duty_ratio=PWMPERIOD*duty;
    duty_ratio=u_add+duty_ratio;
    if(duty_ratio>=PWMPERIOD)
    {
        duty_ratio=PWMPERIOD;
    }
    Print('D');
    Print(duty_ratio/100+48);
    Print(duty_ratio%100/10+48);
    Print(duty_ratio%10+48);
    Print('\n');
}

//ds18b20初始化程序
//返回值:1初始化成功,0失败
uchar Init_DS18B20(void)
{
    uchar x=0;
    dq = 1;    //dq复位
    DelayUs(8);  //稍做延时
    dq = 0;    //单片机将dq拉低
    DelayUs(80); //精确延时 大于 480us
    dq = 1;    //拉高总线
    DelayUs(14);
    x=dq;      //稍做延时后 如果x=0则初始化成功 x=1则初始化失败
    if(x)
    {
        x=0;
    }else
    {
        x=1;
    }
   
    DelayUs(20);
    return x;
}

//串口初始化程序
void Init_Uart()
{
    SCON = 0x52;            //8bit无奇偶校验
    TMOD = 0x20;            //Timer1设为8bit自动重装模式
    TCON=0x00;
    PCON=0X00;
    TH1 = TL1 = -(FOSC/12/32/BAUD); //定时器初值
    TR1 = 1;                //Timer1使能
        RI=0;
    TI=0;
    ES = 1;                 //串口中断使能
    EA = 1;                 //总中断使能
}

//定时器初始化程序
void InitTimer()        //5毫秒
{
    //AUXR &= 0x7F;     //定时器时钟12T模式
    TMOD &= 0x01;       //设置定时器模式
    TL0 = 0x00;     //设置定时初值
    TH0 = 0xEE;     //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    ET0 = 1;
}

//从DS18B20读取一节数据
//返回值:读到的数据
uchar ReadOneChar(void)
{
    uchar i=0;
    uchar dat = 0;
    for (i=8;i>0;i--)
    {
        dq = 0; // 给脉冲信号
        dat>>=1;
        dq = 1; // 给脉冲信号
        if(dq)
        dat|=0x80;
        DelayUs(4);
    }
    return(dat);
}

//对18b20写一个字节
//参数:数据
void WriteOneChar(unsigned char dat)
{
    uchar i=0;
    for (i=8; i>0; i--)
    {
        dq = 0;
        dq = dat&0x01;
        DelayUs(2);
        dq = 1;
        dat>>=1;
    }
}

//读取温度
void ReadTemperature(void)
{
    uchar a=0;
    uchar b=0;
    uchar Data_L=0;
    uchar num=0;

    Init_DS18B20();

    WriteOneChar(0xCC); // 跳过读序号列号的操作
    WriteOneChar(0x44); // 启动温度转换
    Init_DS18B20();
    WriteOneChar(0xCC); //跳过读序号列号的操作
    WriteOneChar(0xBE); //读取温度寄存器

    a=ReadOneChar();  //读低8位
    b=ReadOneChar(); //读高8位

    tx[0] = (a/16+b*16)/10;      //整数部分
    tx[1] = (a/16+b*16)%10;

    Data_L=a&0X0F;
    for(num=3;num<7;num++)       //小数部分
   {
        Data_L=Data_L*10;      
        tx[num]=Data_L/16;           
        Data_L=Data_L%16;      
    }
    temp_now=(uchar)(tx[0*10+tx[1]);
    //temp_now_dec=tx[0]*10+tx[1]+tx[3]*0.1+tx[4]*0.01;
}

//处理命令
void DealCMD()
{
    if(cmd_flag==3)
        {
            switch (cmd)
            {
            case 'U':
            //up命令,温度设定值上升
                if(temp_set<50)
                {
                    temp_set++;
                }else
                {
                    temp_set=50;
                }
                cmd_flag=0;
                break;
            case 'D':
            //down命令,温度设定值下降
                if(temp_set>20)
                {
                    temp_set--;
                }else
                {
                    temp_set=20;
                }
                cmd_flag=0;
                break;
            case 'C':
            //close命令,关闭加热
                run=0;
                //P1=0XF0;
                cmd_flag=0;
                break;
            case 'O':
            //open命令,开启加热
                run=1;
                //P1=0X10;
                cmd_flag=0;
                break;
            default:
                cmd_flag=0;
                break;
            }
        }
}

//串口中断函数
void Get()interrupt 4
{
    uchar dat;
    //P1=0X0C;
    if(RI)//接收到数据
    {
        die=0;
        RI=0;
        dat=SBUF;

        switch (cmd_flag)   //根据当前状态采取不同措施
        {
        case 0:
        //未接收到命令头状态
            if(dat=='$')
            //收到命令头
            {
                cmd_flag=1;
            }
            break;
        case 1:
        //收到命令头状态
            cmd=dat;
            cmd_flag=2;
            break;
        case 2:
        //准备收结束位
            if(dat=='/')
            {
                cmd_flag=3;
            }else
            {
                cmd_flag=0;
            }
            break;
        default:
        //无效状态
            cmd_flag=0;
            break;
        }
    }
}

//定时中断函数
void Time()interrupt 1
{
  time_length++;
    if(time_length>=PWMPERIOD)
    {
        ReadTemperature();
        if(run)
        {
            pwm_out=OPEN;
            time_length=0;
            CalculatePID();
        }
    }else if (time_length>=duty_ratio)
    {
        pwm_out=CLOSE;
    }
   
}

//主函数
int main()
{
    IP=0x10;
    InitTimer();
    Init_Uart();
    Init_DS18B20();
    time_length=0;
    duty_ratio=0;
    e_k=0;
    e_k_1=0;
    e_k_2=0;
//  duty=0;
    temp_set=30;
    time_length=0;
    kp=1;
    ki=0;
    kd=0;
    run=1;
    while(1)
    {
        DealCMD();
        if(run)//判断是否需要启动了加热
        {
            die=!die;           //指示灯闪烁
            TR0=1;
            Print('>');         //运行状态
        }else
        {
            die=0;              //指示灯常亮
            //TR0=0;
            pwm_out=0;
            Print('|');         //暂停状态
        }
        //输出当前温度
        Print('N');
//      Print('=');
//      Print(temp_now/10+48);
//      Print(temp_now%10+48);
//      Print('.');
//      Print((int)(temp_now_dec*10)+48);
//      Print((int)(temp_now_dec*100)%10+48);
        Print(tx[0+'0');
        Print(tx[1+'0');
        Print('.');
        Print(tx[3+'0');
        Print(tx[4+'0');
        Print(',');
        //Print('\n');
        //输出设定温度
        Print('S');
    //  Print('=');
        Print((int)temp_set/10+48);
        Print((int)temp_set%10+48);
        Print('\r');
        Print('\n');
        DelayMs(200);
    }
    return 0;
}

USER.7z

84.56 KB, 下载次数: 27, 下载积分: 黑币 -5

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享分享 分享淘帖 顶 踩
回复

使用道具 举报

沙发
学徒工--静 发表于 2019-7-25 19:45 来自手机 | 只看该作者
不错,谢谢楼主分享
回复

使用道具 举报

板凳
三sansan 发表于 2019-7-28 22:44 | 只看该作者
谢谢分享
回复

使用道具 举报

地板
ZZU2017 发表于 2021-4-6 17:41 | 只看该作者
能否详细讲一下PID的编程思路?
回复

使用道具 举报

5#
c51单片机小白 发表于 2021-4-12 13:25 | 只看该作者
请问楼主模块是用的什么?求分享
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表