1、系列目录
- 基本计时实验
- 输入捕获实验(实验3的基础)
- 电容按键检测实验
- 输出PWM实验
- PWM驱动无刷电机实验
2、程序设计分析
本次我们采用按键控制无刷电机的转速,实验本质是通过按键中断改变CCR的值,从而使PWM的占空比跟随按键改变,将PWM信号输入电调,最终实现对无刷电机的控制。
3、实验用具
- 正点原子STM32F1精英板
- 新西达30A无刷电调
- A2212 1000KV无刷电机
4、程序设计分析
程序设计可以分为三大块:
- 定时器、按键配置
- 电机控制程序
- 按键中断服务函数
1、定时器、按键的配置:
定时器选择TIM4,因为TIM4的四个通道引脚刚好都在板子上,并且不需要重定义端口,比较方便,四个通道分别为PB6、7、8、9。
原子的板子上总共有KEY0(PE4),KEY1(PE3),KEY_UP(PA0)三个按键。刚好可以配置为加速键(KEY1)、减速键(KEY0)、刹车键(PA0)。
2、电机控制程序:
根据按键,改变写入CCR寄存器的值,CCR/ARR的比值及为输出PWM的占空比。直接通过TIMx->CCR1 = x;写入CCR的值。这是寄存器变成的一种方法,可以直接给CCR寄存器赋值,很好用。
3、按键中断函数,这里可以设置两个变量ccr_val和add_val。ccr_val代表输出占空比5%的PWM的CCR的值。电调通过5%-10%的PWM信号控制电机0-100%的转速。所以占空比最高为10%的PWM。add_val是每次按键增加或者减少的CCR。最后写入寄存器的CCR=ccr_val+add_val。
电机控制流程
电路连接——开发板上电,电机上电——电调校对油门——按键——电机启动
如果不理解电调校对油门以及相关操作,点击这里
5、代码示例,很多思想一看程序就明白
1、TIM初始化
//MOT_GPIO PB6,7,8,9
#define MOT_GPIO GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9
#define MOT_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define MOT_GPIO_CLK RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO
#define MOT_TIM TIM4
#define MOT_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define MOT_TIM_CLK RCC_APB1Periph_TIM4
void PWM_TIM_GPIO_Config(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
MOT_GPIO_APBxClock_FUN(MOT_GPIO_CLK,ENABLE);
MOT_TIM_APBxClock_FUN(MOT_TIM_CLK,ENABLE);
//PB6,7,8,9 配置
GPIO_InitStructure.GPIO_Pin = MOT_GPIO;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//TIM5基础配置
//计时器时钟 = APB总线时钟/分频因子
// psc
TIM_TimeBaseStructure.TIM_Period = arr;//重装载值/最大计数值
TIM_TimeBaseStructure.TIM_Prescaler = psc;//预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(MOT_TIM,&TIM_TimeBaseStructure);
}
void PWM_OC_Config(u16 ccr)
{
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_Pulse = ccr;
//前左电机 CCR1 PB6
TIM_OC1Init(MOT_TIM, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(MOT_TIM, TIM_OCPreload_Enable); //使能MOT_TIM在CCR1上的预装载寄存器
//前右电机 CCR2 PB7
TIM_OC2Init(MOT_TIM, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(MOT_TIM, TIM_OCPreload_Enable); //使能MOT_TIM在CCR2上的预装载寄存器
//后左电机 CCR3 PB8
// TIM_OC3Init(MOT_TIM, &TIM_OCInitStructure);
// TIM_OC3PreloadConfig(MOT_TIM, TIM_OCPreload_Enable); //使能MOT_TIM在CCR3上的预装载寄存器
//后右电机 CCR4 PB9
TIM_OC4Init(MOT_TIM, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(MOT_TIM, TIM_OCPreload_Enable); //使能MOT_TIM在CCR4上的预装载寄存器
TIM_ARRPreloadConfig(MOT_TIM, ENABLE); //使能MOT_TIM在ARR上的预装载寄存器
TIM_Cmd(MOT_TIM, ENABLE); //使能MOT_TIM外设
}
void PWM_Config(void)
{
PWM_TIM_GPIO_Config(2000,720-1);
PWM_OC_Config(200);
}
2、电机驱动,实验初期先不需要考虑各个CCR的准确值,后期加入PID之后可以通过PID精确控制每个电机
#include "bsp_motor.h"
#include "delay.h"
#include "usart.h"
#include "key.h"
#include "led.h"
#include "beep.h"
void MOT_Control(u16 ccr1,u16 ccr2,u16 ccr3,u16 ccr4)
{
TIM4->CCR1 = ccr1;
TIM4->CCR2 = ccr2;
TIM4->CCR3 = ccr3;
TIM4->CCR4 = ccr4;
}
3、按键中断服务函数,通过这个函数控制CCR的改变
(1)中断配置
static void EXTI_Config(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);//加速键PE3
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);//减速键PE4
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);//停止键PA0
EXTI_InitStructure.EXTI_Line = EXTI_Line3 | EXTI_Line4 | EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
static void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
//加速键PE3
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//减速键PE4
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//停止键PA0
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void KEY_EXTI_Config(void)
{
EXTI_Config();
NVIC_Config();
EXTI_ClearFlag(EXTI_Line0);
EXTI_ClearFlag(EXTI_Line3);
EXTI_ClearFlag(EXTI_Line4);
}
(2)中断服务函数
u16 ccr_val = 100;//5%的CCR,起始状态
int add_val = 0;//每次按键CCR增加的值
int a =0;
//实际设置写入的CCR=ccr_val+add_val
//加速
void EXTI3_IRQHandler(void)
{
delay_ms(10);
if(EXTI_GetITStatus(EXTI_Line3) != RESET)
{
if(KEY1 == 1)
{
add_val += 5;
LED1 = !LED1;
if(add_val<81)//最高允许加速到80%
{
MOT_Control(ccr_val+add_val,ccr_val+add_val,ccr_val+add_val,ccr_val+add_val);
printf("3CCR = %d\r\n",TIM_GetCapture1(TIM4));
}
else
{
BEEP;
add_val = 80;
}
}
EXTI_ClearITPendingBit(EXTI_Line3);
}
}
//减速
void EXTI4_IRQHandler(void)
{
delay_ms(10);
if(EXTI_GetITStatus(EXTI_Line4) != RESET)
if(KEY0 == 1)
{
add_val -=5;
printf("%d\r\n",add_val);
LED0 = !LED0;
if(add_val > -1)
{
MOT_Control(ccr_val+add_val,ccr_val+add_val,ccr_val+add_val,ccr_val+add_val);
printf("4CCR = %d\r\n",TIM_GetCapture1(TIM4));
}
else
{
BEEP;
add_val = 0;
printf("add_val已归零,add_val = %d\r\n",add_val);
}
}
EXTI_ClearITPendingBit(EXTI_Line4);
}
//停止
void EXTI0_IRQHandler(void)
{
delay_ms(10);
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
if(KEY1 == 1)
{
BEEP = !BEEP;
MOT_Control(ccr_val,ccr_val,ccr_val,ccr_val);
printf("0CCR = %d\r\n",TIM_GetCapture1(TIM4));
}
printf("刹车成功\r\n");
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
6、实验现象
可以看到加速成功,绿色LED灯闪烁,电机速度加快。
串口输出CCR,ccr_val和add_val正常。
7、总结
PWM控制无刷电机的使用在显示中非常广泛,还可以控制步进电机、伺服电机。现在的高精度加工制造产业都脱离不了伺服电机,所以先学习好PWM控制无刷电机这种简单的控制,才能为以后打好基础。后期我将会把四旋翼无人机的设计上传,本节课的控制电机实验就是无人机所需要使用的电机控制程序。