接着上一篇,我要先补完利用PWM对步进电机进行调速的实验
PWM主要有两大要素:频率&占空比
频率
简单来说就是信号在1s内由高电平跳转到低电平再跳转回高电平的次数,一高一低再一高,这便是一个脉冲,一个脉冲对应一个步距角,一般步进电机的步距角分为1.8°和0.9°,因此我们需要转动多少°只需要调节脉冲数即可,而步进电机的转速则是受到脉冲发送频率的影响
占空比
占空比就是在一个PWM周期中高电平占整个周期的比值,一个PWM周期即为1/PWM频率,对于直流电机来说,其是由正负极控制运动,因此占空比越大电机转速越快,对于步进电机来说占空比影响的是电机的转矩,如果占空比过小可能会出现电机无法转动的情况
电机驱动
现在再重新细说一下电机驱动的原理,这里由L298N为例
步进电机工作原理
这是一个二相四线电机,其分为转子和定子,在二者上带有均分的小齿,不妨假设转子齿数为60,定子齿数为40(每个磁极的极弧上有10个小齿),这样定子上的齿距为θ = 360°/60 = 6°
不难得出这样的设计会发生齿错位现象,即当定子A的某一齿对准转子齿时,B相的齿就会与转子偏移[(60-40)/40]*θ = 3°,因此每个通电脉冲驱动电机转动3°,按照一定的节拍规律就可以实现电机的正转反转以及角度。而驱动四线两相电机,四个状态为1、AB正电压;2、A-B正电压;3、A-B-正电压;4、AB-正电压。也就是说我们只需要控制4个IO的电平信号就可以实现对电机的控制
L298N电机驱动
考虑到我之前用的智控驱动集成度过高,不适合初学的我学习,我找了一个很常见的电机驱动模块
L298N是意法半导体集团旗下量产的一种电机驱动芯片,拥有工作电压高、输出电流大、驱动能力强、发热量低、抗干扰能力强等特点,通常用来驱动继电器、螺线管、电磁阀、直流电机以及步进电机。
具体信息可以翻阅芯片手册
回到我们的红板模块上
其由一个L298N电机驱动芯片以及78M05稳压芯片组成,前者用于驱动电机,后者提供电流过热保护,对应引脚如下,详情可以跳转这个链接查看L298N 电机驱动板 - 详细介绍 - 知乎 (zhihu.com)
这款芯片模块可以带两个直流电机或者一个二相四线步进电机,这里标出的接口指的是步进电机的接线
如果是正常驱动电机,则ENA和ENB的两个跳线帽都不用拔掉,若要通过PWM波进行驱动,则需要将PWM通道接到ENA靠近丝印一侧的引脚上
工程以及代码
建立工程
这样就可以生成工程了
代码部分
#define L298NA1(state) HAL_GPIO_WritePin(GPIOF,GPIO_PIN_7,state)
#define L298NA2(state) HAL_GPIO_WritePin(GPIOF,GPIO_PIN_8,state)
#define L298NB1(state) HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,state)
#define L298NB2(state) HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,state)
//定义信号线对应到电机的每个节拍号
void Pulse(unsigned char nInputData ,unsigned char nDirection) //电机走一个步距角(四个节拍)
{
if(nDirection == 1)
{
switch (nInputData) {
case 1:
{
L298NA1(0);
L298NA2(1);
L298NB1(0);
L298NB2(1);
}
break;
case 2:
{
L298NA1(0);
L298NA2(1);
L298NB1(1);
L298NB2(0);
}
break;
case 3:
{
L298NA1(1);
L298NA2(0);
L298NB1(1);
L298NB2(0);
}
break;
case 4:
{
L298NA1(1);
L298NA2(0);
L298NB1(0);
L298NB2(1);
}
break;
}
}
else if(nDirection == 0)
{
switch (nInputData) {
case 1:
{
L298NA1(1);
L298NA2(0);
L298NB1(0);
L298NB2(1);
}
break;
case 2:
{
L298NA1(1);
L298NA2(0);
L298NB1(1);
L298NB2(0);
}
break;
case 3:
{
L298NA1(0);
L298NA2(1);
L298NB1(1);
L298NB2(0);
}
break;
case 4:
{
L298NA1(0);
L298NA2(1);
L298NB1(0);
L298NB2(1);
}
break;
}
}
}
uint8_t Key_on() //按键检测
{
uint8_t KeyNum=0;
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==RESET)
{
HAL_Delay(20);
KeyNum = 1;
}
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)==RESET)
{
HAL_Delay(20);
KeyNum = 2;
}
return KeyNum;
}
void Motor_Start(unsigned char n,unsigned char nDirection) //电机启动函数,n代表要走多少个步距角,dir代表方向
{
uint16_t i;
uint8_t j;
for(i=0;i<=n;i++)
{
for(j=0;j<=4;j++)
{
Pulse(j,nDirection);
HAL_Delay(2);
}
}
}
void Motor_Start_PWM(unsigned char nDirection) //PWM模式。调用前要先打开PWM通道
{
uint8_t j;
for(j=0;j<=4;j++)
{
Pulse(j,nDirection);
HAL_Delay(2);
}
}
int main(void)
{
/* USER CODE BEGIN 1 */
uint16_t arr; //初始化存储装载值变量
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1); //打开PWM通道
arr = __HAL_TIM_GET_AUTORELOAD(&htim3); //获取当前装载值
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
Key = Key_on(); //检测按键
switch (Key) //对应模式
{
case 1:dir = !dir;Key = 0;break; //翻转方向
case 2:
arr += 50;
__HAL_TIM_SET_AUTORELOAD(&htim3,arr); //设置新的装载值
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1, __HAL_TIM_GET_AUTORELOAD(&htim3)/2); //确保占空比始终不变
Key = 0;break; //电机减速
}
Motor_Start_PWM(dir); //启动电机
}
/* USER CODE END 3 */
}
实验现象
不出以外电机就开始转动了,经测试方向控制以及调速部分均无问题