找回密码
 立即注册

QQ登录

只需一步,快速开始

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

基于51单片机实现的电机PWM控制及测速(Proteus仿真)

[复制链接]
跳转到指定楼层
楼主
ID:351566 发表于 2018-6-14 08:44 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
#include <reg52.h>
#include <string.h>
typedef unsigned char uchar;
typedef unsigned int uint;
sbit m1 = P2^0;                //pwm信号1
sbit m2 = P2^1;                //pwm信号2
sbit key0 = P3^3;                //反接制动
sbit key1 = P3^4;                //方向切换及启动
sbit key2 = P3^5;                //能耗制动
sbit key3 = P3^6;                //加速
sbit key4 = P3^7;                //减速
sbit BUTTON0 = P3^2;        //外部中断0
//sbit BUTTON1 = P3^3;        //外部中断1
sbit RS = P0^2;
sbit RW = P0^1;
sbit EN = P0^0;
sbit relay = P0^3;
sbit led  = P2^7;
unsigned char flag=1,sflag=0;                //flag:转向标志,1为正转、0为翻转
                                                                        //sflag:启动标志,1为转动状态、0为停止状态
unsigned int t1=0;                                        //t1:定时器中断计数
int time=10;                                                //time:占空比(0~10)
int freq,num;                                                // num是外部中断计数值,freq保存外部中断的次数
char cnt=0;                                                        // 定时器0 1s计数值
uint pp;                                                         // 定时器0 1s计数值,根据程序修改,本程序取pp=20时定时满1s
void delayms(unsigned int xms);         //微秒级延时函数
void key();                                                        //按键功能函数
void Initial();                                                   //初始化函数,包括定时器0(中断),定时器1,外部中断
void Read_Busy();                                         //读写检查函数
void Write_Cmd(uchar cmd);                  //写指令函数
void Write_Dat(uchar dat);                  //写入数据
void LCD1602_Init();                                //LCD初始化
void PrintStr(char *str);                        // LCD输出字符串
void DisplayNum(int m);                                // LCD显示数值
void delay(unsigned int x);
/*****************主函数***********************/
void main()
{
   m1=1;
   m2=1;
   Initial();                // 初始化定时器0,1,外部中断
   LCD1602_Init();          // 初始化LCD1602
   relay = 0;                // 正常工作情况下继电器不能得电
   led = 0;
   while(1)
   {
      key();
      Write_Cmd(0x80 | 0x00);        // 显示第1行
      PrintStr("Speed:");
      DisplayNum(freq);
      PrintStr(" rpm");
      Write_Cmd(0xc0| 0x00);        // 显示第二行
      PrintStr("Duty:");
      DisplayNum(time*10);
      Write_Dat('%');
   }
}
/************初始化函数****************
*
* 功能描述:实现对定时器0,定时器1,外部中断1中断
*
************************************/
void Initial()
{
   TMOD=0X11;        //设置定时器0 和 定时器1,工作方式1,16为定时
   TH1 = 0XFC;        //定时器0初值1ms
   TL1 = 0X66;
   IP = 0X00;        // 中断优先级控制
   //定时器1初值50ms
   TH0 = 0x4c;        //高8位
   TL0 = 0X00;        //低8位
   EX0 = 1;                // 开启外部中断0,默认中断优先级为0
   EX1 = 1;                // 开启外部中断0,默认中断优先级为2
   IT0 = 1;                // 外部中断触发方式为脉冲触发
   TR0 = 1;                // 定时器0启动由后面启动
   //TR1 = 1;                // 定时器1用来参数PWM波,按下启动按钮后才启动
   ET0 = 1;                // 开定时器0中断
   ET1 = 1;                // 开定时器1中断
   EA=1;                // 开总中断
}
/********************************************
*
* 函数描述:定时器1中断函数,默认中断编号3
*
*            t1                        PWM波
      |-----|                |-----|
      |            |                |     |
      |     |                |     |
----- |     |-----------|     |---------
      |<--------------->|
                 time
********************************************/
void timer1() interrupt 3
{
        TH1=0XFC;
        TL1=0X66;
        t1++;
        if(t1<=time)
        {
                if(flag==1)                //正转,P2^0 输出PWM
                {
                        m1=1;
                        m2=0;
                }
                else                        //反转,P2^1 输出PWM
                {
                        m1=0;
                        m2=1;
                }
        }
        else
        {
                m1=1;
                m2=1;
        }
        if(t1==10)                //定时满 1ms
        {
                t1=0;
        }
}
/**
*
* 函数描述:定时器0中断函数,默认中断编号1,处理
*                 外部中断的数值
*
*/
void timer0() interrupt 1
{
   TH0 = 0x4C;        //定时器1初值50ms
   TL0 = 0X00;
   pp++;
   if(pp ==20)        //计时满 1s
   {
     freq = num/6-25;        //获取1s内中断计数的值num,并进行数值处理,这个根据个人不同需求修改
      if(freq<=0)
         freq=0;
     num = 0;
     //cnt++;
     //led = ~led;
     pp = 0;
   }
}
/**
* @brief        外部中断0,P3^2口每来一次脉冲就计数值加1
               记录特定时间内的脉冲数,可用作频率计
*
*/
void extern_int0(void) interrupt 0         //外部中断0
{
   //led = ~led;
   num ++;        // 外部中断计数
}
/**
* @brief        外部中断1,控制反接制动,反接约2s(软件延时)
*
*/
void extern_int1(void) interrupt 2        //外部中断1
{
      TR1 = 0;                //关闭定时器1,即关闭PWM输出
      if(flag)                //判断是由正转停止还是由反转状态下停止,
      {
         m1 = 0;
         m2 = 1;        //反接制动
         delayms(2000);        //延时2s
         m1 = 1;        //停止时m1,m2都输出高电平
         sflag=0;
      }
      else if(!flag)
      {
         m1 = 1;
         m2 = 0;
         delayms(2000);
         m2 = 1;
         sflag=0;
      }
}
/*
//判断液晶忙,如果忙则等待,因为1602也是一个CPU,要处理原来的指令,如果不判断会导致数据紊乱
void Read_Busy() //读写检查函数
{
    uchar busy;
    P1 = 0xFF;   //P0口作为数据端
    RS = 0;         
    RW = 1;             //读状态的操作时序为 RS=L,RW=H,E=H,D0~D7输出状态字
    do
    {
        EN = 1;
        busy = P1;
        EN = 0;
    }while(busy & 0x80);
    //状态字为busy(8位2进制数)的最高位,
    //若为1则禁止读写,为0则允许读写,该状态用busy&0x80的结果表示
}
*/
/*******************************************
*
* 函数描述:写指令函数
*
*******************************************/
void Write_Cmd(uchar cmd)  //写指令函数
{
    //Read_Busy();        //对控制器每次进行读写操作都要判断是否正忙,即要进行读写检测
   P1 = cmd;           //写入十六进制形式的指令(command)
   RS = 0;
   EN = 0;
   delay(10);
   EN = 1;             //写指令的操作时序:RS=0,RW=0,EN=高脉冲
   delay(10);
   EN = 0;                 //获得高脉冲后使能端重新置零
}
void Write_Dat(uchar dat)  //写入数据
{
   // Read_Busy();         //写入数据前进行读写检测
   P1 = dat;            //P0口写入数据
   RS = 1;
   EN = 0;
   delay(10);
   EN = 1;                  //写数据操作时序:RS=0,RW=0,EN=高脉冲
   delay(10);
   EN = 0;                  //获得高脉冲后使能端重新置零
}
void LCD1602_Init()
{
   RW = 0;
   Write_Cmd(0x38);//设置16*2显示
   delay(8);
   Write_Cmd(0x0f);//开显示 显示光标,光标闪烁
   delay(8);
   Write_Cmd(0x01);//清屏
   delay(8);
   Write_Cmd(0x06);//地址指针移位命令
   delay(8);
    //Write_Cmd(0x80 | 0x00);//显示地址,0x80是第一行的的首地址。0x80|0x06表示数据从第一行第7个字符位置开始显示
}
/**
* @brief        LCD1602打印字符串的函数,使用到了strlen函数,需要引入string.h头文件
*
*/
void PrintStr(char *str)        // LCD输出字符串
{
   char i,len;
   len = strlen(str);                 // 获取字符串长度
   for(i=0;i<len;i++)
   {
      Write_Dat(*str);
      str++;
   }
}
/**
* @brief        LCD1602显示整数,显示4位数
* @note       
*            希望补充显示浮点型数值,未完成功能
*/
void DisplayNum(int m)                // LCD显示数值
{
   int disdata[4];                // 显示占空比的数值,存到此数组
   disdata[0] = m/1000+0x30;
   disdata[1] = m%1000/100+0x30;
   disdata[2] = m%100/10+0x30;
   disdata[3] = m%10+0x30;
   if(disdata[0]==0x30)                //如果千位为0,让LCD不显示,0x30和0x20可以查询ACSII码表
   {
      disdata[0]=0x20;
      if( disdata[1]==0x30)                //如果百位为0,让LCD不显示,0x30和0x20可以查询ACSII码表
      {
         disdata[1]=0x20;
         if( disdata[2]==0x30)                //如果百位为0,让LCD不显示,0x30和0x20可以查询ACSII码表
         {
            disdata[2]=0x20;
         }
      }
   }
   Write_Dat(disdata[0]);
   delay(4);
   Write_Dat(disdata[1]);
   delay(4);
   Write_Dat(disdata[2]);
   delay(4);
   Write_Dat(disdata[3]);
   delay(4);
}
/**
*
* @brief        按键功能函数,包括启动/反转,能耗制动,加速,减速
*
*/
void key()
{
        if(key1==0)                //转向及启动
        {
                delayms(5);
                if(key1==0)
                {
                        while(key1==0);
                        if(sflag==1)                //为了实现停止后再启动保持之前的转向
                        {
                                flag=!flag;
                                TR1=1;                //开启定时器1,即开始PWM波输出
                        }
                        else
                        {
                                TR1=1;
                                sflag=1;
                        }
                }
        }
        else if(key2==0)                //停止
        {
                delayms(5);
                if(key2==0)
                {
                        while(key2==0);
                        relay=1;        //能耗制动
                        TR1=0;                //关闭定时器1,即关闭PWM输出
                        m1=1;
                        m2=1;
                        sflag=0;
                }
        }
        else if(key3==0)                //加速
        {
                delayms(5);
                if(key3==0)
                {
                        while(key3==0);
                        if(time>=10)
                                time=10;
                        else
                                time++;
                }
        }
        else if(key4==0)                //减速
        {
                delayms(5);
                if(key4==0)
                {
                        while(key4==0);
                        if(time==0)
                                time=0;
                        else
                                time--;
                }
        }
        /*
        else if(key0==0)        // 按下反接制动
        {
           delayms(5);                //软件消抖
           if(key0==0)                //再次检测到按下
           {
              while(key0==0);
              TR1 = 0;                //关闭定时器1,即关闭PWM输出
             
              if(flag)                //判断 是由正转停止还是由反转状态下停止
              {
                 m1 = 0;
                 m2 = 1;        //反接制动
                // delayms(10);        //延时1.5s
                 m1 = 1;        //停止时m1,m2都输出高电平
                 sflag=0;
              }
              else if(!flag)
              {
                 m1 = 1;
                 m2 = 0;
                // delayms(150);
                 m2 = 1;
              }
           }
        }
        */
}
/**
* @brief        软件延时函数
*
*/
void delayms(unsigned int xms)
{
        unsigned int i,j;
        for(i=xms;i>0;i--)
                for(j=110;j>0;j--);
}
/**
* @brief        软件延时函数
*
*/
void delay(unsigned int x)
{
        unsigned int a,b;
        for(a=x;a>0;a--)
                for(b=10;b>0;b--);
}

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

使用道具 举报

沙发
ID:634862 发表于 2019-11-23 18:14 | 只看该作者
怎么没有电路图呢
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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