目录
价格:
一个是安森美的,一个是德州仪器的,同为步进电机驱动芯片,LV8731V差不多是DRV8825的两倍价格。当然,市面上的同类芯片很多,但驱动方式都是差不多的,都是用PWM输入到step脚控制步进,其它引脚配置相应的功能,比如说细分,输出电流等等。我这里使用的是LV8731V,所以以LV8731V为例。
引脚简介:
这款分别有驱动直流电机和步进电机两种模式,这里以驱动步进电机为例,用这款芯片驱动直流电机属实大材小用,想看直流电机驱动的可以看我这篇文章:http://t.csdnimg.cn/O9EMx
电平标准:
引脚配置:
这里我只写一些关键的引脚功能及配置,其它详细信息大家可以去看手册,附上LV8731V的中文手册:通过百度网盘分享的文件:步进电机驱动LV8731V.pdf
链接:https://pan.baidu.com/s/14_tquvj3gTdAwXWe0dxH4g?pwd=3569
提取码:3569·设定工作/待机
这里我们在电路接VDD(3.3V),配置成工作模式。
·设定驱动电机种类
这里我们在电路上接GND,配置成STM模式。
·设定励磁模式
这里我把MD2接的VDD,MD1接的单片机,当MD1低时为W1-2相励磁(4细分),高时为4W1-2相励磁(16细分)和模式。最终我使用的时候是配置成的16细分,毕竟有最多的就用最多的,来都来了,你懂的。
·设定衰减比从而控制输出电流
可以看出,输出电流由参考电压和RF电阻共同决定。
·设定间隙频率
·设定输出使能
接在单片机上。
·设定正、反转
接在单片机上。
·短路检测
接在单片机上,当检测到这个引脚为低电平时判断电机短路。
硬件电路:
这里我用到了两个步进,所以用两块芯片
MX配置:
我这里使用的单片机是STM32G0B1CBTX,其它款的STM32单片机也是一样的。
用到了两个通道:
配置定时器的可以看下这篇文章:http://t.csdnimg.cn/6Ca7D
这里对定时时长的配置没有那么严格,我们在代码里会重新更改分频和计数次数以更改电机速度。
PWM配置:
DMA配置:
DMA配置可以看一下我这篇文章:http://t.csdnimg.cn/Wk0vc
代码:
我这里把代码分成了DrvLV8731V和AppLV8731V,一个为驱动层,一个为应用层,AppLV8731V对DrvAT8870再封装了一次供用户调用。
DrvLV8731V.H
#ifndef _DRV_LV8731V_H_ #define _DRV_LV8731V_H_ #include "DrvSys.h" #define ONCE_PWM_CNT 100 //一次传送的pwm个数 //GPIO配置 //LV8731V输出使能,低电平使能 #define LV8731VEnable1() HAL_GPIO_WritePin(OE1_GPIO_Port, OE1_Pin, GPIO_PIN_RESET) #define LV8731VEnable2() HAL_GPIO_WritePin(OE2_GPIO_Port, OE2_Pin, GPIO_PIN_RESET) #define LV8731VDisable1() HAL_GPIO_WritePin(OE1_GPIO_Port, OE1_Pin, GPIO_PIN_SET) #define LV8731VDisable2() HAL_GPIO_WritePin(OE2_GPIO_Port, OE2_Pin, GPIO_PIN_SET) //LV8731V励磁模式配置,低电平4细分,高电平16细分 #define LV8731V_4divide_1() HAL_GPIO_WritePin(MODE1_1_GPIO_Port, MODE1_1_Pin, GPIO_PIN_RESET) #define LV8731V_4divide_2() HAL_GPIO_WritePin(MODE1_2_GPIO_Port, MODE1_2_Pin, GPIO_PIN_RESET) #define LV8731V_16divide_1() HAL_GPIO_WritePin(MODE1_1_GPIO_Port, MODE1_1_Pin, GPIO_PIN_SET) #define LV8731V_16divide_2() HAL_GPIO_WritePin(MODE1_2_GPIO_Port, MODE1_2_Pin, GPIO_PIN_SET) //LV8731V输出电流配置,低电平100%,高电平50% #define LV8731V_100I_1() HAL_GPIO_WritePin(ATT2_1_GPIO_Port, ATT2_1_Pin, GPIO_PIN_RESET) #define LV8731V_100I_2() HAL_GPIO_WritePin(ATT2_2_GPIO_Port, ATT2_2_Pin, GPIO_PIN_RESET) #define LV8731V_50I_1() HAL_GPIO_WritePin(ATT2_1_GPIO_Port, ATT2_1_Pin, GPIO_PIN_SET) #define LV8731V_50I_2() HAL_GPIO_WritePin(ATT2_2_GPIO_Port, ATT2_2_Pin, GPIO_PIN_SET) //LV8731V方向控制,低电平正转 #define LV8731VFrForeward1() HAL_GPIO_WritePin(FR1_GPIO_Port, FR1_Pin, GPIO_PIN_RESET) #define LV8731VFrForeward2() HAL_GPIO_WritePin(FR2_GPIO_Port, FR2_Pin, GPIO_PIN_RESET) #define LV8731VFrReversal1() HAL_GPIO_WritePin(FR1_GPIO_Port, FR1_Pin, GPIO_PIN_SET) #define LV8731VFrReversal2() HAL_GPIO_WritePin(FR2_GPIO_Port, FR2_Pin, GPIO_PIN_SET) //LV8731V短路EMO信号读取 #define LV8731VReadEmo1() HAL_GPIO_ReadPin(EMO1_GPIO_Port, EMO1_Pin) #define LV8731VReadEmo2() HAL_GPIO_ReadPin(EMO2_GPIO_Port, EMO2_Pin) enum { LV0 = 0, //升降电机 LV1, //晶向电机 LVNum };//LV8731V驱动的个数 void PWMStart(u8 idx, u8 fr, u32 cstep); void PWMStop(u8 idx); void SetPWMFre(u8 idx, u16 cnt); u8 GetLV8731VEmo(u8 idx);
DrvLV8731V.C
#include "DrvLV8731V.h" /************************************************** 说明: 使用PWM+DMA输出指定频率、指定占空比、指定个数的脉冲。 LV8731V的频率0~13K, **************************************************/ static struct { u16 pwm_dma_cnt; //进行的DMA次数 u16 pwmcnt_residue; //剩余的pwm脉冲数 u16 callback_mode; //回调方式 0:什么都不执行 1:直接关闭PWM(=<100) 2:(>100) }LV8731VPara[2]={0}; static u32 Data[LVNum][ONCE_PWM_CNT];//发送区缓存 /******************************* 开始发送pwm,DMA模式 idx:电机索引 0升降电机 1晶向电机 fr:旋转方向 0正转 1反转 指定的pwm数量 0一直发送pwm 非0:发送对应个数的pwm *******************************/ void PWMStart(u8 idx, u8 fr, u32 cstep) { switch(idx) { case 0: LV8731VEnable1(); //电机使能 if(fr) //方向 LV8731VFrReversal1(); else LV8731VFrForeward1(); if(cstep == 0) { LV8731VPara[LV0].callback_mode = 0; HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, Data[LV0], ONCE_PWM_CNT); } else { if(cstep < 100) { LV8731VPara[LV0].callback_mode = 1; HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, Data[LV0], cstep); } else { LV8731VPara[LV0].callback_mode = 2; LV8731VPara[LV0].pwm_dma_cnt = cstep / 100; LV8731VPara[LV0].pwmcnt_residue = cstep % 100; HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, Data[LV0], ONCE_PWM_CNT); } } break; case 1: LV8731VEnable2(); if(fr) LV8731VFrReversal2(); else LV8731VFrForeward2(); if(cstep == 0) { LV8731VPara[LV1].callback_mode = 0; HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_1, Data[LV1], ONCE_PWM_CNT); } else { if(cstep <= 100) { LV8731VPara[LV1].callback_mode = 1; HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_1, Data[LV1], cstep); } else { LV8731VPara[LV1].callback_mode = 2; LV8731VPara[LV1].pwm_dma_cnt = cstep / 100; LV8731VPara[LV1].pwmcnt_residue = cstep % 100; HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_1, Data[LV1], ONCE_PWM_CNT); } } break; default: break; } } /******************************* 停止发送pwm,DMA模式 0:升降电机 1:晶向电机 *******************************/ void PWMStop(u8 idx) { switch(idx) { case 0: HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_1); LV8731VDisable1(); //失能 break; case 1: HAL_TIM_PWM_Stop_DMA(&htim2, TIM_CHANNEL_1); LV8731VDisable2(); break; default: break; } } /******************************* 设置pwm的频率 0升降电机 1晶向电机 *******************************/ void SetPWMFre(u8 idx, u16 cnt) { u16 i; if(idx!=0 && idx!=1) return; //__HAL_TIM_SET_PRESCALER(&htim3, prescaler); //配置PSC预分频值 if(idx == 0) __HAL_TIM_SET_AUTORELOAD(&htim3, cnt); //配置PWM频率 ARR else __HAL_TIM_SET_AUTORELOAD(&htim2, cnt); for(i = 0; i < ONCE_PWM_CNT; i++) { Data[idx][i] = cnt/2; //占空比50% } } /******************************* 获取短路警告信号 idx:电机索引号 0升降电机 1晶向电机 返回1为警告 *******************************/ u8 GetLV8731VEmo(u8 idx) { u8 level; if(idx == 0) level = LV8731VReadEmo1(); else level = LV8731VReadEmo2(); if(level == 0) return 1; return 0; } /******************************* pwm中断回调,DMA模式,全发送完才进中断 *******************************/ void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef * htim) { u8 lv_idx; if(htim == &htim3) lv_idx = LV0; else if(htim == &htim2) lv_idx = LV1; if(LV8731VPara[lv_idx].callback_mode == 0) //什么也不执行 { } else if(LV8731VPara[lv_idx].callback_mode == 1) //直接关闭pwm { if(lv_idx == LV0) { HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_1); //LV8731VDisable1();//如果想要电机停止后任然保持使能状态就注释掉 } else { HAL_TIM_PWM_Stop_DMA(&htim2, TIM_CHANNEL_1); LV8731VDisable2(); } } else if(LV8731VPara[lv_idx].callback_mode == 2) //step个数大于100的情况 { if(LV8731VPara[lv_idx].pwm_dma_cnt == 1) //等于1时就要判断了 { if(LV8731VPara[lv_idx].pwmcnt_residue == 0) { if(lv_idx == LV0) { HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_1); LV8731VDisable1(); } else { HAL_TIM_PWM_Stop_DMA(&htim2, TIM_CHANNEL_1); LV8731VDisable2(); } } else { if(lv_idx == LV0) { HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, Data[lv_idx], LV8731VPara[lv_idx].pwmcnt_residue);//停止再打开不会有时间间隙 } else { HAL_TIM_PWM_Stop_DMA(&htim2, TIM_CHANNEL_1); HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_1, Data[lv_idx], LV8731VPara[lv_idx].pwmcnt_residue);//停止再打开不会有时间间隙 } LV8731VPara[lv_idx].pwmcnt_residue = 0; } } else { LV8731VPara[lv_idx].pwm_dma_cnt--; } } }
这里DMA选择一次发送100个是减小数组大小,如果发送大于一百的pwm个数则在回调函数里循环发送,回调函数是HAL库里给的函数,一次DMA传输完成便进一次。
AppLV8731V.H
#ifndef _APP_LV8731V_H_ #define _APP_LV8731V_H_ #include "DrvLV8731V.h" #include "AppSystemConfig.h" /****************操作***************************/ void AppLV8731VSetSpeed(u8 idx, u16 speed); void AppLV8731VStart(u8 idx, u8 fr, u32 cstep); void AppLV8731VCircleStart(u8 idx, u8 fr, u16 r_cnt); void AppLV8731VPositionStart(u8 idx, u8 fr, u16 len_or_angle); void AppLV8731VStop(u8 idx); void AppLV8731VResetLocation(u8 idx); void AppLV8731VAngleStart(u16 angle); /****************初始化***************************/ void AppLV8731VInit(void); /****************主循环接口***************************/ void AppLV8731VProcess(void); #endif
AppLV8731V.C
#include "AppLV8731V.h" /************************************************** 说明: **************************************************/ static s32 location[LVNum];//电机位置 /*************************** 设置速度,单位:r/s ***************************/ void AppLV8731VSetSpeed(u8 idx, u16 speed) { float load_cnt; load_cnt = 100000 / (speed * SysCfg.Lv[idx].egear_in) - 1; SetPWMFre(idx,(u16)load_cnt);//设置初始速度 } /*************************** 按指定步数开始旋转 0:升降电机 1:晶向电机 0:正转(升,逆) 1:反转 0:一直旋转 非0:旋转指定个step ***************************/ void AppLV8731VStart(u8 idx, u8 fr, u32 cstep) { s32 temp = 0; if(cstep != 0) { temp = location[idx]; if(fr == 1) location[idx] += cstep; else location[idx] -= cstep; if(idx == 1) { if(location[idx] > 110592)//限位300度 { location[idx] = location[idx] % 110592; PWMStart(idx, 0, (u32)(temp - location[idx]));//反转回去 return; } } } PWMStart(idx, fr, cstep); } /*************************** 按指定圈数开始旋转 0:升降电机 1:晶向电机 0:正转 1:反转 0:一直旋转 非0:旋转指定圈数 ***************************/ void AppLV8731VCircleStart(u8 idx, u8 fr, u16 r_cnt) { if(SysCfg.Lv[idx].egear_in == 0) return; AppLV8731VStart(idx, fr, (r_cnt * SysCfg.Lv[idx].egear_in)); } /*************************** 按距离开始运动 0:升降电机 1:晶向电机 0:升(逆) 1:降(顺) 0:一直运动 非0:升降距离(mm) 旋转角度(度) ***************************/ void AppLV8731VPositionStart(u8 idx, u8 fr, u16 len_or_angle) { if(SysCfg.Lv[idx].egear_out * SysCfg.Lv[idx].egear_in == 0) return; AppLV8731VStart(idx, fr, (len_or_angle * SysCfg.Lv[idx].egear_out * SysCfg.Lv[idx].egear_in)); } /*************************** 晶向电机旋转至指定角度 ***************************/ void AppLV8731VAngleStart(u16 angle) { s16 step = 0; u16 ch_angle = 0; u8 fr; if(SysCfg.Lv[LV1].egear_out * SysCfg.Lv[LV1].egear_in == 0) return; step = angle * SysCfg.Lv[LV1].egear_out * SysCfg.Lv[LV1].egear_in - location[LV1]; fr = (step>=0) ? 1:0; step = abs(step); ch_angle = step / (SysCfg.Lv[LV1].egear_out * SysCfg.Lv[LV1].egear_in); AppLV8731VPositionStart(LV1, fr, ch_angle); } /*************************** 停止 0:升降电机 1:晶向电机 ***************************/ void AppLV8731VStop(u8 idx) { PWMStop(idx); } /*************************** LV8731V初始化 ***************************/ void AppLV8731VInit(void) { LV8731V_16divide_1(); LV8731V_16divide_2(); LV8731V_50I_1(); LV8731V_50I_2(); AppLV8731VSetSpeed(LV0,SysCfg.Lv[LV0].speed); AppLV8731VSetSpeed(LV1,SysCfg.Lv[LV1].speed); } /*************************** LV8731V短路警告检测,主循环接口 ***************************/ void AppLV8731VProcess(void) { DevStatus.Lv[LV0].emo = GetLV8731VEmo(0); DevStatus.Lv[LV1].emo = GetLV8731VEmo(1); } /*************************** 复位位置 0升降 1晶向 ***************************/ void AppLV8731VResetLocation(u8 idx) { location[idx] = 0; }
这段代码有对电机2的限位及记位置操作,用不上的可以删去。已经应用层的一些数据操作,也可以删去,AppLV8731VProcess()函数放主循环里。
上机可以正常运行。