l298n电机驱动模块 电机正反转 电机调速
1.当驱动电压(板子标识为VCC输入,实际可以接受的输入范围是7-12V。一般都是使用航模电池或买电池盒。
在电机驱动,单片机,编码器电机三者使用的过程中,一定要把三者的GND连在一起,也就是共地!!!
【电工知识】编码器是干什么用的,这下终于明白了!
编码器, 是将信号或数据进行编制、转换为可用以通讯、传输和存储的信号形式的设备。编码器把角位移或直线位移转换成电信号。是工业中常用的电机定位设备,可以精确的测试电机的角位移和旋转位置。 编码器最直接的作用就是可以测量位移,知道位移了就可以计算得到速度了。
简单点,编码器是用来测速的。放在电机上,可以解算出电机的转速。
按输出信号分:增量式和绝对式
它们存着最大的区别:在增量编码器的情况下,位置是从零位标记开始计算的脉冲数量确定的,而绝对型编码器的位置是由输出代码的读数确定的。绝对型编码器并不与实际的位置分离。如果电源再次接通,那么位置读数仍是当前的、有效的,不像增量编码器那样,必须去寻找零位标记。
绝对式掉电不丢失读数值,增量式会丢失。
按传感技术分:光电式和霍尔式(激光式、磁式、感应式、电容式等)
1.光电式:就像下图里的一样。当光线透过齿轮盘时,接收器会计数一次,并再传送给CPU。不同的转速,自然在相同时间内的计数值不一样。进而达到测速的功能。(个人理解)
2.霍尔式
【Arduino 101】霍尔编码器与闭环控制(白话)
霍尔编码器是由霍尔码盘和霍尔元件组成。霍尔码盘是在一定直径的圆板上等分地布置有不同的磁极。霍尔码盘与电动机同轴,电动机旋转时,霍尔元件检测输出若干脉冲信号,为判断转向,一般输出两组存在一定相位差的方波信号 (通常相差90°,我们称为AB相)。
测速还是计算相同时间内的脉冲数,而判断转向呢?
可以看到下图,当顺时针旋转时,A相处在下降沿时,B相是高电平。A相处在上升沿时,B相是低电平;当逆时针旋转时,(即把上面的波形从右向左看)A相处在下降沿时,B相是低电平。A相处在上升沿时,B相是高电平。进而判断正反转。
编码器 | 单片机 |
---|---|
A相 | 接单片机的脉冲检测接口 |
B相 | 接单片机的脉冲检测接口 |
+5V | +5V |
GND | GND |
编码器 | 电机驱动 |
---|---|
M+ | 接电机驱动的输出OUT1 |
M- | 接电机驱动的输出OUT2 |
单片机 | 电机驱动 |
---|---|
会根据两个方法来说明 | IN1 |
/ | IN2 |
/ | ENA |
GB37520 电机为减速电机。即电机头部有一个减速器,电机转动带动减速器里的齿轮后再通过减速器的输出轴输出相应的转速。比如减速比是 1: 30,表示电机转动 30圈,电机的减速器转动一圈。
这里的磁环是固定在直流电机转轴上的,与减速电机的输出轴是不一样的。减速电机的输出轴是经过减速齿轮变换后的,之前有介绍到该电机的减速比为:1:30, 所以, 如果减速电机输出轴旋转一圈,实际上可以检测到的编码器每相脉冲数量为:13*30=390 个。 再通过单片机的编码器接口4 倍频就是 1560个脉冲信号, 这在我们计算实际位移和速度非常有用。
算出来的n是直流电机的转速,正常来说可以使用了,但我使用的是减速电机。要想得到真正输出轴的转速,还需要除减速比。
此时的计数值1000,可以理解为最大输出电压数字量是1000。如果我的供电电压是12V,并在程序中设置PWM的输出值为800。则驱动电机的电压为9.6V。 在电机控制中,电压越大,电机转速越快,而通过PWM输出不同的模拟电压,便可以使电机达到不同的输出转速。
2.开启GPIO中断,并打开NVIC选项卡设置中断优先级
编码器自带了上拉电阻,所以无需外部上拉,可以直接连接到单片机I0读取。(我点上拉了,不过也没关系)
3.开启串口,检验效果
/* USER CODE BEGIN PV */
int32_t Sum=0;
int32_t Last_sum=0; //计算脉冲数目
float V_rotate=0; //转子速度(圈/ms),电机速度等于转子/减速比
float V_Set; //设定速度
float V_Out=0; //减速电机速度
/* USER CODE END PV */
/* USER CODE BEGIN 0 */
int fputc(int c,FILE *stream)
{
uint8_t ch[1]={c};
HAL_UART_Transmit(&huart1,ch,1,0xFFFF);
return c;
}
int8_t a=0; //检测是否进入中断
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//外部中断
{
a=1;
/******一倍频*******/
if(GPIO_Pin==GPIO_PIN_1)//编码器计数,检测到PF1(A相)跳变沿
{
if (HAL_GPIO_ReadPin(GPIOF,GPIO_PIN_2)==1)//PF2(B相)高电平正转
Sum++;
else
Sum--;
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
/* USER CODE END 2 */
while (1)
{
__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_1,700);
Last_sum=Sum;
HAL_Delay(100);
V_rotate=(Sum-Last_sum)/1.30f; //转速(圈/s)
V_Out=V_rotate/30;//电机速度单位(转/秒)
printf("V_Out is %f\r\n",V_Out);
}
可以看到输出的效果还是很好的。没有效果的看看是不是少接线了。
参考博客:STM32 Cubemax(六) —— STM32利用定时器编码器模式处理带编码器直流电机
这里可以延续上面的工程内容,需要修改以下几个方面。
这里的分配系数表示的对计数值分频,如果你这里写了3=4-1,那后面speed算的时候就不用除以4了。编码器模式默认四倍频。
编码器上的AB相应该接编码器对应的引脚。 编码器模式就相当于一个专门计数的工具,当编码器的码盘转动时,A,B相输出矩形波脉冲,进入PE9和PE11进行计数(数脉冲)。
2.修改GPIO引脚和模式
3.开启TIM2_CHANNEL1
这里注意一下,最好编码器的更新中断定时器,要比10ms定时器的优先级高,可以防止在更新中打被打断。
电机驱动的输入IN1和IN2接输出GPIO,PE12和PE10,单片机输出的PWM信号接到电机驱动的ENA(使能端A,上下都行)。这样的话一路PWM通过IN1,IN2的状态来控制PWM输出信号输出到OUT1还是OUT2,进而控制正反转。
0.基本函数测试
HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL);//打开定时器的encoder模式
Direction = __HAL_TIM_IS_TIM_COUNTING_DOWN(&htim1);//可以获得当前电机的转向
enc1 = (__HAL_TIM_GET_COUNTER(&htim1));//获取encoder编码器的计数值
如果debug模式下enc1读数为0或为最大20000,建议接串口,用串口输出数据。
1.在阅读上面的博客完成配置后
motor.h:
#ifndef __MOTOR_H
#define __MOTOR_H
#include "main.h"
#include "tim.h"
#include "PID.h"
#define RR 30u //电机减速比
#define RELOADVALUE __HAL_TIM_GetAutoreload(&htim1) //获取自动装载值,本例中为20000
#define COUNTERNUM __HAL_TIM_GetCounter(&htim1) //获取编码器定时器中的计数值
#define IN1(state) HAL_GPIO_WritePin(GPIOE,GPIO_PIN_12,(GPIO_PinState)(state)) //IN1
#define IN2(state) HAL_GPIO_WritePin(GPIOE,GPIO_PIN_10,(GPIO_PinState)(state)) //IN2
//电机结构体
typedef struct _Motor
{
int32_t lastAngle; //上10ms转过的角度
int32_t totalAngle; //总的角度
int16_t loopNum; //防超上限
float speed; //电机输出轴目前转速,单位为RPM
float set;
}Motor;
extern Motor motor;
void Motor_Init(void);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
void Clockwise_rotation(void);
void Counterclockwise_rotation(void);
void stop(void);
#endif
motor.c:
#include "motor.h"
Motor motor;
void Motor_Init(void)
{
HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL); //开启编码器定时器
__HAL_TIM_ENABLE_IT(&htim1,TIM_IT_UPDATE); //开启编码器定时器更新中断,防溢出处理
HAL_TIM_Base_Start_IT(&htim6); //开启10ms定时器中断
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //开启PWM
__HAL_TIM_SET_COUNTER(&htim1, 10000); //编码器定时器初始值设定为10000
motor.loopNum = 0; //防溢出
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==htim6.Instance) //10ms中断
{
int16_t pluse = COUNTERNUM - RELOADVALUE/2;
motor.totalAngle = pluse + motor.loopNum * RELOADVALUE/2;
motor.speed = (float)(motor.totalAngle - motor.lastAngle)/(4*13*RR)*6000; //进行速度计算,根据前文所说的,4倍频,编码器13位,减速比30,再乘以6000即为每分钟输出轴多少转
motor.lastAngle = motor.totalAngle; //更新转过的圈数
}
else if(htim->Instance == htim1.Instance) //如果是编码器更新中断,即10ms内,脉冲数超过了计数范围,需要进行处理
{
if(COUNTERNUM < 10000) motor.loopNum++;
else if(COUNTERNUM > 10000) motor.loopNum--;
__HAL_TIM_SetCounter(&htim1, 10000); //重新设定初始值
}
}
void Clockwise_rotation(void)
{
IN1(1);
IN2(0);
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_1,2000);
}
void Counterclockwise_rotation(void)
{
IN1(0);
IN2(1);
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_1,2000);
}
void stop()
{
IN1(0);
IN2(0);
}
关于这两行代码的理解
int16_t pluse = COUNTERNUM - RELOADVALUE/2;
motor.totalAngle = pluse + motor.loopNum * RELOADVALUE/2;
而且编码器上的AB相也应该接编码器对应的引脚。
这里我们波动编码器上的磁盘,可以看到Angle在发生变化。
至此,你肯定对电机驱动和编码器,以及简单的操控电机有所了解。后续博客会更新经典控制算法PID的原理和代码。