找回密码
 立即注册

QQ登录

只需一步,快速开始

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

51单片机电机驱动及电机速度电流测量程序

[复制链接]
跳转到指定楼层
楼主
ID:688286 发表于 2022-5-5 16:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
电机驱动电路使用H桥可逆斩波电路,通过采样电阻和旋转编码器测量电机转速和电流

单片机代码如下:
  1. /*
  2. 设计思路
  3. 电机驱动电路:使用H桥可逆斩波电路去驱动电机转动,该电路可在网上查找学习相关知识。
  4. 电机的调速可通过改变PWM波的占空比实现,当占空比为50%时,电机不转,
  5.                                                                                                                                                          当占空比大于50%时电机正转,并且越大转速越快,但不要超过80%,超过可能会使电路发烫
  6.                                                                                                                                                          当占空比小于50%时电机反转,并且越小转速越快,但不要超过20%,超过可能会使电路发烫                       
  7. 测电流是在驱动电路两个下桥臂和电源地之间加了一个采样电阻,通过AD转换读取采样电阻电压得到电流
  8. 测电机转速是利用电机上的旋转编码器,编码器为96线编码器,所以电机转一圈会发出96个脉冲
  9. 使用单片机的外部中断,设置触发方式为边沿触发,记录脉冲数
  10. 使用单片机的定时器中断,每50ms产生一次中断,在中断里读取当前脉冲数,计算转速,之后脉冲数置0
  11. 根据直流电机的电流与转矩之间的关系,计算电机的转矩
  12. 转矩和扭矩是一个物理量,只是在不同的场合称呼不同
  13. */
  14. #include "reg52.h"
  15. #include "iic.h"
  16. #include "oled.h"
  17. #include<stdio.h>
  18. #include<intrins.h>
  19. #define uchar unsigned char
  20. #define  PCF8591 0x90    //PCF8591 地址
  21. #define  qCt         12.75
  22. uchar data_byte;
  23. u8 RH,RL,TH,TL,U8FLAG;

  24. sbit PWM1=P1^2;//接IN1 控制正转

  25. u8 duty = 7 ; //定义占空比,H桥可逆斩波电路在50%占空比时电机不转,大于50%正转,小于50%反转,
  26.                //6表示占空比为70%,不要修改占空比,不同占空比下电机功率不同,导致扭矩计算错误
  27. u8 time;
  28. int pluse = 0 ;    //电机编码器的脉冲数
  29. float Speed = 0.0 ;   //速度

  30. #define  NOP()   _nop_()   /* 定义空指令 */
  31. #define  _Nop()  _nop_()   /*定义空指令*/


  32. sbit     m_SCL=P1^4;       //I2C  时钟
  33. sbit     m_SDA=P1^3;       //I2C  数据
  34. bit ack;                 /*应答标志位*/
  35.    

  36. /*******************************************************************
  37.                      起动总线函数               
  38. 函数原型: void  Start_I2c();  
  39. 功能:     启动I2C总线,即发送I2C起始条件.  
  40. ********************************************************************/
  41. void Start_I2c()
  42. {
  43.   m_SDA=1;         /*发送起始条件的数据信号*/
  44.   _Nop();
  45.   m_SCL=1;
  46.   _Nop();        /*起始条件建立时间大于4.7us,延时*/
  47.   _Nop();
  48.   _Nop();
  49.   _Nop();
  50.   _Nop();   
  51.   m_SDA=0;         /*发送起始信号*/
  52.   _Nop();        /* 起始条件锁定时间大于4μs*/
  53.   _Nop();
  54.   _Nop();
  55.   _Nop();
  56.   _Nop();      
  57.   m_SCL=0;       /*钳住I2C总线,准备发送或接收数据 */
  58.   _Nop();
  59.   _Nop();
  60. }

  61. /*******************************************************************
  62.                       结束总线函数               
  63. 函数原型: void  Stop_I2c();  
  64. 功能:     结束I2C总线,即发送I2C结束条件.  
  65. ********************************************************************/
  66. void Stop_I2c()
  67. {
  68.   m_SDA=0;      /*发送结束条件的数据信号*/
  69.   _Nop();       /*发送结束条件的时钟信号*/
  70.   m_SCL=1;      /*结束条件建立时间大于4μs*/
  71.   _Nop();
  72.   _Nop();
  73.   _Nop();
  74.   _Nop();
  75.   _Nop();
  76.   m_SDA=1;      /*发送I2C总线结束信号*/
  77.   _Nop();
  78.   _Nop();
  79.   _Nop();
  80.   _Nop();
  81. }

  82. /*******************************************************************
  83.                  字节数据发送函数               
  84. 函数原型: void  SendByte(UCHAR c);
  85. 功能:     将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
  86.           此状态位进行操作.(不应答或非应答都使ack=0)     
  87.            发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
  88. ********************************************************************/
  89. void  SendByte(unsigned char  c)
  90. {
  91. unsigned char  BitCnt;

  92. for(BitCnt=0;BitCnt<8;BitCnt++)  /*要传送的数据长度为8位*/
  93.     {
  94.      if((c<<BitCnt)&0x80)m_SDA=1;   /*判断发送位*/
  95.        else  m_SDA=0;               
  96.      _Nop();
  97.      m_SCL=1;               /*置时钟线为高,通知被控器开始接收数据位*/
  98.       _Nop();
  99.       _Nop();             /*保证时钟高电平周期大于4μs*/
  100.       _Nop();
  101.       _Nop();
  102.       _Nop();         
  103.      m_SCL=0;
  104.     }
  105.    
  106.     _Nop();
  107.     _Nop();
  108.     m_SDA=1;                /*8位发送完后释放数据线,准备接收应答位*/
  109.     _Nop();
  110.     _Nop();   
  111.     m_SCL=1;
  112.     _Nop();
  113.     _Nop();
  114.     _Nop();
  115.     if(m_SDA==1)ack=0;     
  116.        else ack=1;        /*判断是否接收到应答信号*/
  117.     m_SCL=0;
  118.     _Nop();
  119.     _Nop();
  120. }

  121. /*******************************************************************
  122.                  字节数据接收函数               
  123. 函数原型: UCHAR  RcvByte();
  124. 功能:        用来接收从器件传来的数据,并判断总线错误(不发应答信号),
  125.           发完后请用应答函数应答从机。  
  126. ********************************************************************/   
  127. unsigned char   RcvByte()
  128. {
  129.   unsigned char  retc;
  130.   unsigned char  BitCnt;
  131.   
  132.   retc=0;
  133.   m_SDA=1;                     /*置数据线为输入方式*/
  134.   for(BitCnt=0;BitCnt<8;BitCnt++)
  135.       {
  136.         _Nop();           
  137.         m_SCL=0;                  /*置时钟线为低,准备接收数据位*/
  138.         _Nop();
  139.         _Nop();                 /*时钟低电平周期大于4.7μs*/
  140.         _Nop();
  141.         _Nop();
  142.         _Nop();
  143.         m_SCL=1;                  /*置时钟线为高使数据线上数据有效*/
  144.         _Nop();
  145.         _Nop();
  146.         retc=retc<<1;
  147.         if(m_SDA==1)retc=retc+1;  /*读数据位,接收的数据位放入retc中 */
  148.         _Nop();
  149.         _Nop();
  150.       }
  151.   m_SCL=0;   
  152.   _Nop();
  153.   _Nop();
  154.   return(retc);
  155. }

  156. /********************************************************************
  157.                      应答子函数
  158. 函数原型:  void Ack_I2c(bit a);
  159. 功能:      主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
  160. ********************************************************************/
  161. void Ack_I2c(bit a)
  162. {
  163.   
  164.   if(a==0)m_SDA=0;              /*在此发出应答或非应答信号 */
  165.   else m_SDA=1;
  166.   _Nop();
  167.   _Nop();
  168.   _Nop();      
  169.   m_SCL=1;
  170.   _Nop();
  171.   _Nop();                    /*时钟低电平周期大于4μs*/
  172.   _Nop();
  173.   _Nop();
  174.   _Nop();  
  175.   m_SCL=0;                     /*清时钟线,钳住I2C总线以便继续接收*/
  176.   _Nop();
  177.   _Nop();   
  178. }


  179. /*******************************************************************
  180. DAC 变换, 转化函数               
  181. *******************************************************************/
  182. bit DACconversion(unsigned char sla,unsigned char c,  unsigned char Val)
  183. {
  184.    Start_I2c();              //启动总线
  185.    SendByte(sla);            //发送器件地址
  186.    if(ack==0)return(0);
  187.    SendByte(c);              //发送控制字节
  188.    if(ack==0)return(0);
  189.    SendByte(Val);            //发送DAC的数值  
  190.    if(ack==0)return(0);
  191.    Stop_I2c();               //结束总线
  192.    return(1);
  193. }

  194. /*******************************************************************
  195. ADC发送字节[命令]数据函数               
  196. *******************************************************************/
  197. bit ISendByte(unsigned char sla,unsigned char c)
  198. {
  199.    Start_I2c();              //启动总线
  200.    SendByte(sla);            //发送器件地址
  201.    if(ack==0)return(0);
  202.    SendByte(c);              //发送数据
  203.    if(ack==0)return(0);
  204.    Stop_I2c();               //结束总线
  205.    return(1);
  206. }
  207. unsigned char IRcvByte(unsigned char sla)
  208. {  unsigned char c;

  209.    Start_I2c();          //启动总线
  210.    SendByte(sla+1);      //发送器件地址
  211.    if(ack==0)return(0);
  212.    c=RcvByte();          //读取数据0

  213.    Ack_I2c(1);           //发送非就答位
  214.    Stop_I2c();           //结束总线
  215.    return 0;
  216. }
  217. float Get_Current(unsigned char s)      //读取电流
  218. {
  219.         float t ;
  220.         t = IRcvByte(s) ;
  221.         if(Speed != 0 )
  222.         {if(Speed <= 250)
  223.                 {
  224.         t = 7.8 -0.027*Speed;
  225.         return t ;
  226.                 }else
  227.                 {return 0.1;}
  228.         }
  229.         else
  230.         {
  231.          return 0 ;
  232.         }
  233. }

  234. void int0() interrupt 0 using 0//外部中断0的执行程序。
  235. {                                                            //interrupt 0指外部中断0 using 0指第0组寄存器                                                 
  236.         pluse++ ;
  237. }

  238. void timer0_init()
  239. {
  240.         TMOD=0x01;//定时器0工作方式1 定时器1工作方式1
  241.        
  242.         TH0=0xff;
  243.         TL0=0xa3;//定时50ms
  244.        
  245.         TH1=0x4b;
  246.         TL1=0xff;//定时0.1ms
  247.        
  248.         IT0=1;//外部中断0为跳变沿触发方式   
  249.        
  250.   EA=1;//开总中断
  251.   EX0=1;//打开外部中断0   
  252.        
  253.         ET0=1;//打开定时器0中断开关
  254.         TR0=1;//打开定时器0运行开关
  255.         ET1=1;//打开定时器0中断开关
  256.         TR1=1;//打开定时器0运行开关
  257.         PT1 = 0 ;
  258.         PX0 = 1 ;
  259.         PT0 = 1 ;
  260.        
  261. }
  262. void tim0() interrupt 1
  263. {
  264.        TR0=0;                //赋初值时,关闭定时器
  265.        TH0=0xff;       
  266.        TL0=0xa3;        //定时0.1ms
  267.        TR0=1;                //打开定时器
  268.        time++;
  269.        
  270.        if(time>=10) //10*0.1ms=1ms   pwm波一个周期1ms,即1khz
  271.                          {
  272.                                  time=0;       
  273.                          }
  274.        if(time<=duty) PWM1=1;                //点空比70%
  275.        else PWM1=0;   
  276. }

  277. void tim1() interrupt 3   //定时器1的中断服务函数
  278. {
  279.            EX0 = 0 ;//关闭外部中断,停止记录脉冲数
  280.            TR1=0;                //赋初值时,关闭定时器
  281.      TH1=0x4b;       
  282.      TL1=0xff;        //定时50ms
  283.      TR1=1;                //打开定时器
  284.            Speed = pluse*12.5 ;  //将脉冲数转化为转速,编码器为96线,转一圈输出96个脉冲,所以转速=脉冲数/50ms/96*60   ,单位转/分钟
  285.            pluse = 0 ;
  286.                  EX0 = 1 ;//打开外部中断
  287. }


  288. void main()  //主循环
  289. {
  290.   char a[6] ;

  291.   float Current=0.0 ;
  292.         float T = 0.0 ;
  293.         OLED_Init();   //初始化屏幕
  294.         OLED_Clear();
  295.         OLED_ShowString(0,0,"Speed:",12);//显示字符
  296.         OLED_ShowString(95,0,"r/m",12);
  297.         OLED_ShowString(0,3,"I:",12);
  298.         OLED_ShowString(95,3,"A",12);
  299.         OLED_ShowString(0,5,"T:",12);
  300.         OLED_ShowString(95,5,"N*m",12);
  301.         timer0_init();  //配置定时器和外部中断
  302.        
  303.         while(1)
  304.         {
  305.           sprintf(a,"%.1f ",Speed);  //显示速度
  306.                 OLED_ShowString(50,0,a,12);
  307.                
  308.   
  309.                   if(ISendByte(PCF8591,0x40))
  310.                         {
  311.                     Current= Get_Current(PCF8591);  
  312.                         }
  313.                         T =  Current*qCt;      //电机扭矩=电流*转矩常数*磁通量
  314.                
  315.                 sprintf(a,"%.2f  ",Current);  //显示电流
  316.                 OLED_ShowString(40,3,a,12);
  317.                        
  318.        
  319.                 sprintf(a,"%.2f  ",T);  //显示扭矩
  320.                 OLED_ShowString(40,5,a,12);
  321.         }
  322.        
  323. }
复制代码



Keil代码下载: 电机转速电流.zip (350.44 KB, 下载次数: 42)

评分

参与人数 1黑币 +10 收起 理由
admin + 10 共享资料的黑币奖励!

查看全部评分

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

使用道具 举报

沙发
ID:262 发表于 2022-5-5 20:39 | 只看该作者
楼主你好 能分享一下原理图吗?这个是怎么测电流的?用的什么芯片?
回复

使用道具 举报

板凳
ID:688286 发表于 2022-5-5 23:00 | 只看该作者
heicad 发表于 2022-5-5 20:39
楼主你好 能分享一下原理图吗?这个是怎么测电流的?用的什么芯片?

用的采样电阻,在GND和下桥臂之间接一个0.05的采样电阻,把采样电阻上的压降放大20倍,ADC采样之后的电压就是电流

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

地板
ID:56665 发表于 2022-5-6 08:37 | 只看该作者
电机扭矩=电流*转矩常数*磁通量,这个公式中转矩常数是马达规格书上有注明吗,还有磁通量怎么测试的
回复

使用道具 举报

5#
ID:77675 发表于 2022-5-6 12:36 | 只看该作者
能分享一下原理图吗?
回复

使用道具 举报

6#
ID:1058055 发表于 2022-12-17 17:33 | 只看该作者
heicad 发表于 2022-5-5 20:39
楼主你好 能分享一下原理图吗?这个是怎么测电流的?用的什么芯片?

应该是L298或者L297
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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