标题:
51单片机电机驱动及电机速度电流测量程序
[打印本页]
作者:
ascsca
时间:
2022-5-5 16:05
标题:
51单片机电机驱动及电机速度电流测量程序
电机驱动电路使用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)
2022-5-5 16:05 上传
点击文件名下载附件
下载积分: 黑币 -5
作者:
heicad
时间:
2022-5-5 20:39
楼主你好 能分享一下原理图吗?这个是怎么测电流的?用的什么芯片?
作者:
ascsca
时间:
2022-5-5 23:00
heicad 发表于 2022-5-5 20:39
楼主你好 能分享一下原理图吗?这个是怎么测电流的?用的什么芯片?
用的采样电阻,在GND和下桥臂之间接一个0.05的采样电阻,把采样电阻上的压降放大20倍,ADC采样之后的电压就是电流
作者:
m182892
时间:
2022-5-6 08:37
电机扭矩=电流*转矩常数*磁通量,这个公式中转矩常数是马达规格书上有注明吗,还有磁通量怎么测试的
作者:
lipen2008
时间:
2022-5-6 12:36
能分享一下原理图吗?
作者:
愿世间且清尘
时间:
2022-12-17 17:33
heicad 发表于 2022-5-5 20:39
楼主你好 能分享一下原理图吗?这个是怎么测电流的?用的什么芯片?
应该是L298或者L297
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1