电机驱动电路使用H桥可逆斩波电路,通过采样电阻和旋转编码器测量电机转速和电流
单片机代码如下:
- /*
- 设计思路
- 电机驱动电路:使用H桥可逆斩波电路去驱动电机转动,该电路可在网上查找学习相关知识。
- 电机的调速可通过改变PWM波的占空比实现,当占空比为50%时,电机不转,
- 当占空比大于50%时电机正转,并且越大转速越快,但不要超过80%,超过可能会使电路发烫
- 当占空比小于50%时电机反转,并且越小转速越快,但不要超过20%,超过可能会使电路发烫
- 测电流是在驱动电路两个下桥臂和电源地之间加了一个采样电阻,通过AD转换读取采样电阻电压得到电流
- 测电机转速是利用电机上的旋转编码器,编码器为96线编码器,所以电机转一圈会发出96个脉冲
- 使用单片机的外部中断,设置触发方式为边沿触发,记录脉冲数
- 使用单片机的定时器中断,每50ms产生一次中断,在中断里读取当前脉冲数,计算转速,之后脉冲数置0
- 根据直流电机的电流与转矩之间的关系,计算电机的转矩
- 转矩和扭矩是一个物理量,只是在不同的场合称呼不同
- */
- #include "reg52.h"
- #include "iic.h"
- #include "oled.h"
- #include<stdio.h>
- #include<intrins.h>
- #define uchar unsigned char
- #define PCF8591 0x90 //PCF8591 地址
- #define qCt 12.75
- uchar data_byte;
- u8 RH,RL,TH,TL,U8FLAG;
- sbit PWM1=P1^2;//接IN1 控制正转
- u8 duty = 7 ; //定义占空比,H桥可逆斩波电路在50%占空比时电机不转,大于50%正转,小于50%反转,
- //6表示占空比为70%,不要修改占空比,不同占空比下电机功率不同,导致扭矩计算错误
- u8 time;
- int pluse = 0 ; //电机编码器的脉冲数
- float Speed = 0.0 ; //速度
- #define NOP() _nop_() /* 定义空指令 */
- #define _Nop() _nop_() /*定义空指令*/
-
- sbit m_SCL=P1^4; //I2C 时钟
- sbit m_SDA=P1^3; //I2C 数据
- bit ack; /*应答标志位*/
-
- /*******************************************************************
- 起动总线函数
- 函数原型: void Start_I2c();
- 功能: 启动I2C总线,即发送I2C起始条件.
- ********************************************************************/
- void Start_I2c()
- {
- m_SDA=1; /*发送起始条件的数据信号*/
- _Nop();
- m_SCL=1;
- _Nop(); /*起始条件建立时间大于4.7us,延时*/
- _Nop();
- _Nop();
- _Nop();
- _Nop();
- m_SDA=0; /*发送起始信号*/
- _Nop(); /* 起始条件锁定时间大于4μs*/
- _Nop();
- _Nop();
- _Nop();
- _Nop();
- m_SCL=0; /*钳住I2C总线,准备发送或接收数据 */
- _Nop();
- _Nop();
- }
- /*******************************************************************
- 结束总线函数
- 函数原型: void Stop_I2c();
- 功能: 结束I2C总线,即发送I2C结束条件.
- ********************************************************************/
- void Stop_I2c()
- {
- m_SDA=0; /*发送结束条件的数据信号*/
- _Nop(); /*发送结束条件的时钟信号*/
- m_SCL=1; /*结束条件建立时间大于4μs*/
- _Nop();
- _Nop();
- _Nop();
- _Nop();
- _Nop();
- m_SDA=1; /*发送I2C总线结束信号*/
- _Nop();
- _Nop();
- _Nop();
- _Nop();
- }
- /*******************************************************************
- 字节数据发送函数
- 函数原型: void SendByte(UCHAR c);
- 功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
- 此状态位进行操作.(不应答或非应答都使ack=0)
- 发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
- ********************************************************************/
- void SendByte(unsigned char c)
- {
- unsigned char BitCnt;
-
- for(BitCnt=0;BitCnt<8;BitCnt++) /*要传送的数据长度为8位*/
- {
- if((c<<BitCnt)&0x80)m_SDA=1; /*判断发送位*/
- else m_SDA=0;
- _Nop();
- m_SCL=1; /*置时钟线为高,通知被控器开始接收数据位*/
- _Nop();
- _Nop(); /*保证时钟高电平周期大于4μs*/
- _Nop();
- _Nop();
- _Nop();
- m_SCL=0;
- }
-
- _Nop();
- _Nop();
- m_SDA=1; /*8位发送完后释放数据线,准备接收应答位*/
- _Nop();
- _Nop();
- m_SCL=1;
- _Nop();
- _Nop();
- _Nop();
- if(m_SDA==1)ack=0;
- else ack=1; /*判断是否接收到应答信号*/
- m_SCL=0;
- _Nop();
- _Nop();
- }
- /*******************************************************************
- 字节数据接收函数
- 函数原型: UCHAR RcvByte();
- 功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
- 发完后请用应答函数应答从机。
- ********************************************************************/
- unsigned char RcvByte()
- {
- unsigned char retc;
- unsigned char BitCnt;
-
- retc=0;
- m_SDA=1; /*置数据线为输入方式*/
- for(BitCnt=0;BitCnt<8;BitCnt++)
- {
- _Nop();
- m_SCL=0; /*置时钟线为低,准备接收数据位*/
- _Nop();
- _Nop(); /*时钟低电平周期大于4.7μs*/
- _Nop();
- _Nop();
- _Nop();
- m_SCL=1; /*置时钟线为高使数据线上数据有效*/
- _Nop();
- _Nop();
- retc=retc<<1;
- if(m_SDA==1)retc=retc+1; /*读数据位,接收的数据位放入retc中 */
- _Nop();
- _Nop();
- }
- m_SCL=0;
- _Nop();
- _Nop();
- return(retc);
- }
- /********************************************************************
- 应答子函数
- 函数原型: void Ack_I2c(bit a);
- 功能: 主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
- ********************************************************************/
- void Ack_I2c(bit a)
- {
-
- if(a==0)m_SDA=0; /*在此发出应答或非应答信号 */
- else m_SDA=1;
- _Nop();
- _Nop();
- _Nop();
- m_SCL=1;
- _Nop();
- _Nop(); /*时钟低电平周期大于4μs*/
- _Nop();
- _Nop();
- _Nop();
- m_SCL=0; /*清时钟线,钳住I2C总线以便继续接收*/
- _Nop();
- _Nop();
- }
- /*******************************************************************
- DAC 变换, 转化函数
- *******************************************************************/
- bit DACconversion(unsigned char sla,unsigned char c, unsigned char Val)
- {
- Start_I2c(); //启动总线
- SendByte(sla); //发送器件地址
- if(ack==0)return(0);
- SendByte(c); //发送控制字节
- if(ack==0)return(0);
- SendByte(Val); //发送DAC的数值
- if(ack==0)return(0);
- Stop_I2c(); //结束总线
- return(1);
- }
- /*******************************************************************
- ADC发送字节[命令]数据函数
- *******************************************************************/
- bit ISendByte(unsigned char sla,unsigned char c)
- {
- Start_I2c(); //启动总线
- SendByte(sla); //发送器件地址
- if(ack==0)return(0);
- SendByte(c); //发送数据
- if(ack==0)return(0);
- Stop_I2c(); //结束总线
- return(1);
- }
- unsigned char IRcvByte(unsigned char sla)
- { unsigned char c;
- Start_I2c(); //启动总线
- SendByte(sla+1); //发送器件地址
- if(ack==0)return(0);
- c=RcvByte(); //读取数据0
- Ack_I2c(1); //发送非就答位
- Stop_I2c(); //结束总线
- return 0;
- }
- float Get_Current(unsigned char s) //读取电流
- {
- float t ;
- t = IRcvByte(s) ;
- if(Speed != 0 )
- {if(Speed <= 250)
- {
- t = 7.8 -0.027*Speed;
- return t ;
- }else
- {return 0.1;}
- }
- else
- {
- return 0 ;
- }
- }
- void int0() interrupt 0 using 0//外部中断0的执行程序。
- { //interrupt 0指外部中断0 using 0指第0组寄存器
- pluse++ ;
- }
- void timer0_init()
- {
- TMOD=0x01;//定时器0工作方式1 定时器1工作方式1
-
- TH0=0xff;
- TL0=0xa3;//定时50ms
-
- TH1=0x4b;
- TL1=0xff;//定时0.1ms
-
- IT0=1;//外部中断0为跳变沿触发方式
-
- EA=1;//开总中断
- EX0=1;//打开外部中断0
-
- ET0=1;//打开定时器0中断开关
- TR0=1;//打开定时器0运行开关
- ET1=1;//打开定时器0中断开关
- TR1=1;//打开定时器0运行开关
- PT1 = 0 ;
- PX0 = 1 ;
- PT0 = 1 ;
-
- }
- void tim0() interrupt 1
- {
- TR0=0; //赋初值时,关闭定时器
- TH0=0xff;
- TL0=0xa3; //定时0.1ms
- TR0=1; //打开定时器
- time++;
-
- if(time>=10) //10*0.1ms=1ms pwm波一个周期1ms,即1khz
- {
- time=0;
- }
- if(time<=duty) PWM1=1; //点空比70%
- else PWM1=0;
- }
- void tim1() interrupt 3 //定时器1的中断服务函数
- {
- EX0 = 0 ;//关闭外部中断,停止记录脉冲数
- TR1=0; //赋初值时,关闭定时器
- TH1=0x4b;
- TL1=0xff; //定时50ms
- TR1=1; //打开定时器
- Speed = pluse*12.5 ; //将脉冲数转化为转速,编码器为96线,转一圈输出96个脉冲,所以转速=脉冲数/50ms/96*60 ,单位转/分钟
- pluse = 0 ;
- EX0 = 1 ;//打开外部中断
- }
- void main() //主循环
- {
- char a[6] ;
- float Current=0.0 ;
- float T = 0.0 ;
- OLED_Init(); //初始化屏幕
- OLED_Clear();
- OLED_ShowString(0,0,"Speed:",12);//显示字符
- OLED_ShowString(95,0,"r/m",12);
- OLED_ShowString(0,3,"I:",12);
- OLED_ShowString(95,3,"A",12);
- OLED_ShowString(0,5,"T:",12);
- OLED_ShowString(95,5,"N*m",12);
- timer0_init(); //配置定时器和外部中断
-
- while(1)
- {
- sprintf(a,"%.1f ",Speed); //显示速度
- OLED_ShowString(50,0,a,12);
-
-
- if(ISendByte(PCF8591,0x40))
- {
- Current= Get_Current(PCF8591);
- }
- T = Current*qCt; //电机扭矩=电流*转矩常数*磁通量
-
- sprintf(a,"%.2f ",Current); //显示电流
- OLED_ShowString(40,3,a,12);
-
-
- sprintf(a,"%.2f ",T); //显示扭矩
- OLED_ShowString(40,5,a,12);
- }
-
- }
复制代码
Keil代码下载:
电机转速电流.zip
(350.44 KB, 下载次数: 42)
|