• 基于51单片机PCF8591转换电位器数字量为模拟量当作设定速度经PID控制直流电机实际转速的项目工程


    #include <reg51.h>
    #include< intrins.h>
    #define uchar unsigned char
    #define uint unsigned int
    //#define ulong unsigned long
    #define KeyPort P1//自定义按键端
    #define AD_IN0  0x40////自定义PCF8591的AD转换通道1
    #define AD_IN1  0x41//自定义PCF8591的AD转换通道2
    #define AD_IN2  0x42//自定义PCF8591的AD转换通道3
    #define AD_IN3  0x43//自定义PCF8591的AD转换通道4
    #define  CYCLE 100 //限定的最大转速 
    sbit pwm=P2^0;//位定义脉冲输入端口
    sbit IN1=P2^1;//位定义L298输入端1
    sbit IN2=P2^2;//位定义L298输入端2
    sbit SDA=P2^3;//位定义PCF8591的I2C总线数据端
    sbit SCL=P2^4;//位定义PCF8591的I2C总线时钟端
    sbit RS = P2^5;//LCD1602数据命令选择端口 
    sbit RW = P2^6;//LCD1602读写选择端口
    sbit EN = P2^7;//LCD1602使能端口
    sbit zhongduan=P3^2;//位定义中断次数变量
    int out;//声明PID调节后输出偏差值变量 
    uint value;//声明经PID函数PID调节后输出偏差值返回值变量
    uint expc_speed; //设定速度
    uint real_speed; //实际速度
    uint Inlpuse;//脉冲计数变量
    uint time,count;//定时器0中断次数变量、定时器1中断次数变量
    uint Proportion=8;//比例常数 Proportional Const
    uint Integral=3;//积分常数 Integral Const
    uint Derivative=1;//微分常数 Derivative Const 
    uint Error;//Error[0]为声明当前偏差值变量
    uint LastError;//Error[-1]为声明之后偏差值变量
    uint PrevError;//Error[-2]为声明再后偏差值变量
    uchar code table[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//0、1、2、3、4、5、6、7、8、9、A、b、C、d、E、F的显示码
      void delay(void)  
    { 
       uchar i;
       for(i=1;i>0;i--);
      }
      void delay_ms(uchar x)
    {
       uint i,j;
       for(i=x;i>0;i--)
        for(j=50;j>0;j--);
       }
      uchar KeyScan(void)//按键扫描函数
    {
       uchar keyvalue;
       if(KeyPort!=0xff)
     {
        delay_ms(10);
        if(KeyPort!=0xff)
      {
         keyvalue=KeyPort;
         while(KeyPort!=0xff);
         switch(keyvalue)
       {
          case 0xfe:
                    return 1;
                    break;
          case 0xfd:
                    return 2;
                    break;
          case 0xfb:
                    return 3;
                    break;
          case 0xf7:
                    return 4;
                    break;
          case 0xef:
                    return 5;
                    break;
          case 0xdf:
                    return 6;
                    break;
          case 0xbf:
                    return 7;
                    break;
          case 0x7f:
                    return 8;
                    break;
          default:
                  return 0;
                  break;
         }
        }
       }
        return 0;
      }
      void write_com(uchar com)//lcd1602写命令函数
    {
       RS=0;
       RW=0;
       P0=com;
       delay_ms(5);
       EN=1;
       delay_ms(5);
       EN=0;
      }
      void write_data(uchar date)//lcd1602写一个字符函数
    {
       RS=1;
       RW=0;
       P0=date;
       delay_ms(5);
       EN=1;
       delay_ms(5);
       EN=0;
      }
      void lcd_display(uchar position,uint speed)//lcd1602显示函数
    {
       write_com(0x80+position);//LCD1602字符型液晶显示器第一行第显示
       write_data(table[speed]);//显示字符串
       write_com(0x80+0x40+position);//LCD1602字符型液晶显示器第二行显示
       write_data(table[speed]);//显示字符串
      }
      void lcd_init()//lcd1602初始化函数
    {
       write_com(0x38);
       write_com(0x0c);
       write_com(0x06);
       write_com(0x01);
      }
      int PIDControl()//PID偏差计算函数
    {
       Error=expc_speed-real_speed;//计算偏差
       //进行增量式PID计算
       out=out+Proportion*(Error-LastError)+Integral*Error+Derivative*(Error+PrevError-2*LastError);
       //进行误差更新
       PrevError=LastError;
       LastError=Error;
       //上下限幅处理
       if(out<0)
       out=0;//驱动电机的电压不可能小于零,故当输出小于零时,输出应该值为零。
       if(out>255)
       out=255;//DA转换为8位,最大数值为255,故当大于255时,限制为255。
       return out;
      }
      void iic_init()//iic初始化函数       
    {
       SDA=1;
       delay();
       SCL=1;
       delay();    
      }                      
      void start()//iic开始信号函数
    {
       SDA=1;
       delay();
       SCL=1;
       delay();
       SDA=0;
       delay();
      }
      void stop()//iic停止信号函数    
    {
       SDA=0;
       delay();
       SCL=1;
       delay();
       SDA=1;
       delay();
      }                                                                               
      void respons()//iic应答函数 ,相当于一个智能的延时函数。 
    {
       uchar i;
       SCL=1;
       delay();
       while((SDA==1)&&(i<250))
       i++;
       SCL=0;
       delay();
      }
    //  uchar read_byte()//iic读一个字节数据函数
    //{
    //   uchar i,k;
    //   SCL=0;
    //   delay();
    //   SDA=1;
    //   delay();
    //   for(i=0;i<8;i++)
    // {
    //    SCL=1;
    //    delay();
    //    k=(k<<1)|SDA;//先左移一位,再在最低位接受当前位。
    //    SCL=0;
    //    delay();
    //   }
    //   return k;  
    //  }                                                                              
      void write_byte(uchar date)//iic写一字节数据函数      
    {
       uchar i,temp;
       temp=date;
       for(i=0;i<8;i++)
     {
        temp=temp<<1; //左移一位,移出的一位在CY中。
        SCL=0;//只有在scl=0时sda能变化值 
        delay();
        SDA=CY; 
        delay();
        SCL=1; 
        delay();            
       }   
        SCL=0;
        delay();
        SDA=1;
        delay();
       }           
      void write_add(uchar control,uchar date)//PCF8591的DA转换函数  
    { 
       start(); 
       write_byte(0x91);
       respons(); 
       write_byte(control); 
       respons(); 
       write_byte(date);
       respons(); 
       stop();  
      }             
    //  uchar read_add(uchar control)//PCF8591的AD转换函数
    //{
    //   uchar date; 
    //   start();
    //   write_byte(0x91); 
    //   respons(); 
    //   write_byte(control); 
    //   respons(); 
    //   start();
    //   write_byte(0x91+1);//把最后一位变成1,读数据。 
    //   respons(); 
    //   date=read_byte();
    //   stop(); 
    //   return date; 
    //  }
      void SystemInit()//
    {
       TMOD=0x21;//设定时器0为工作方式1,定时器1为工作方式2(自动重装初值)。
       TH0=0x3c;//设定50ms一次中断
       TL0=0xb0;
       TH1=0x9c;//设定100us一次中断
       TL1=0x9c;
       EA=1;//开总中断    
       EX0=1;//开外部中断0
       IT0=1;//启动下降沿触发有效
       ET0=1;//开定时器0中断
       ET1=1;//开定时器1中断
       TR0=1;//启动定时器0
       TR1=1;//启动定时器1
      }
      void exter0() interrupt 0//外部中断0函数
    {
       Inlpuse++;//M法测速度(外部中断0和定时器0用在M法测速上)
      }
      void timer0() interrupt 1//定时器0中断函数
    {
       TH0=0x3c;//重装初值
       TL0=0xb0;
       time++;
       if(time>=20)//1s钟读取一次转速
     {
        EX0=0;
        TR0=0;
        real_speed=Inlpuse;//计算转速
        Inlpuse=0;
        value=PIDControl();
        write_add(0x40,value);//进行DA转换,将数字量转换为模拟量。
        EX0=1;
        TR0=1;
       }
      }
      void time1() interrupt 3//定时器1中断函数
    {
       count++;
       if(count>=100)
       count=0;
       if(count<out)
       pwm=1;
       else
       pwm=0;
      } 
      void main()
    {
       unsigned char num;                 
       SystemInit();
       lcd_init();
       iic_init();                        
       while (1)//主循环
     {
        num=KeyScan();//循环调用按键扫描
        switch (num)
      {
         case 1:
       {
          if(expc_speed<CYCLE)
          expc_speed++;
         } 
         break;
         case 2:
       {
          if(expc_speed>0)
          expc_speed--;
         } 
        } 
       lcd_display(4,expc_speed);
       lcd_display(5,real_speed);   
      }  
     }           
  • 相关阅读:
    重温Delphi之:面向对象
    Delphi2007卸载后无法再安装
    ERP开源框架 + 二次开发平台 介绍
    CodeGear RAD 2007 SP4 最新下载及破解
    Delphi XE中类成员的访问权限(新增了strict private和strict protected,还有automated)
    Delphi学习技巧
    Codeforce 101B. Buses(线段树or树状数组+离散化)
    Codeforce 101B. Buses(线段树or树状数组+离散化)
    codeforce -39E-What Has Dirichlet Got to Do with That?(博弈+dfs)
    codeforce -39E-What Has Dirichlet Got to Do with That?(博弈+dfs)
  • 原文地址:https://www.cnblogs.com/AChenWeiqiangA/p/12850554.html
Copyright © 2020-2023  润新知