直流有刷电机的控制
1、直流有刷电机驱动设计与分析
如下图所示,是使用 4 个三极管搭建的 H 桥电路。
上图中:
当 Q3 和 Q2 导通,Q1和Q4关闭时,电流将经过 Q3 从右往左流过电机,在经过 Q2 流到电源负极,这时图中电机可以逆时针转动;
当 Q1 和 Q4 导通,Q2和Q3关闭时,电流将经过 Q1从左往右流过电机,在经过 Q4 流到电源负极,这时图中电机可以顺时针转动。
当 Q1 和 Q3 导通、Q2和Q4关闭或者当开关Q2和Q4导通、Q1和Q3关闭时直流电机不旋转。此时可以认为电机处于“刹车”状态,电机惯性转动产生的电势将被短路,形成阻碍运动的反电势,形成“刹车”作用。
当Q1 和 Q2 导通或者当Q3 和 Q4 导通时直接电源短路,会烧毁电源,这种情况严禁出现。
当开关Q1、Q2、Q3和Q4四个开关都关闭时,认为电机处于“惰行”状态,电机惯性所产生的电势将无法形成电路,从而也就不会产生阻碍运动的反电势,电机将惯性转动较长时间。
H桥控制电机的3种方式:
简单的开关只能控制电机的正反转,引入PWM控制可以实现方向和速度调节。
调节占空比实现控速,占空比越大平均电压(电流)越大,速度越快。
PWM频率一般在10KHz~20KHz之间。频率太低会导致电机转速过低,噪声较大。频率太高,会因为MOS管的开关损耗而降低系统的效率。
根据不同桥臂的PWM控制方式不同,大致上可以分为三种控制模式:受限单极模式、单极模式、双极模式
A、受限单极模式(高端 PWM 低端 ON)
受限单极模式:电机电枢驱动电压极性是单一的。
优点:控制电路简单。
缺点:不能刹车,不能能耗制动,在负载超过设定速度时不能提供反向力矩。调速静差大,调速性能很差,稳定性也不好。
从上图分析,如果使Q1 连接 PWM 通道,而Q4一直处于导通状态。由于Q4一直处于导通状态,那么经过电机的电流和电压就有Q1的导通时长来决定,增大 Q1 导通的占空比就可以增大电机的转速。这个时候Q2 跟 Q3 是处于关闭状态的。
当需要电机反转的时候,则是Q3 连接到 PWM 通道,而Q2则一直处于导通状态,由于Q2一直处于导通状态,电机的电流和电压有Q3的导通时长决定,也就是 PWM 的占空比,增大连接到Q3 的 PWM的占空比,即可改变电机的电压,从而改变电机转速,这个时候Q1和Q4处于关闭状态。
B、单极模式
单极模式:电机电枢驱动电压极性是单一的
优点:启动快,能加速,刹车,能耗制动,能量反馈,调速性能不如双极模式好,但是相差不多,电机特性也比较好。在负载超速时也能提供反向力矩。
缺点:刹车时,不能减速到0,速度接近0速度时没有制动力。不能突然倒转。动态性能不好,调速静差稍大。
PWM和PWMN是互补的PWM信号,一般用高级控制定时器的通道和互补通道控制。
(1)电流采集电路
电流采集使用将电阻串接在低端MOS管的下方,通过电机的电流经过电阻会产生一定的压降,然后使用超高精度、低噪声、零漂移双通道运放MAX44248放大电压信号。MAX44248在较宽的供电范围内保持超低功耗特性,器件集成了专有的自动调零电路,通过连续测量、补偿输入失调消除随时间、温度变化产生的漂移以及 1/f噪声的影响。器件还集成了EMI滤波器,以减小输出信号上的高频解调。运放采用2.7V至36V单电源供电,或士1.35V至±18V双电源供电。器件为单位增益稳定,具有1MHz增益带宽积,每路运放仅消耗90μA 电流。在驱动板上使用的是5V单电源供电,而另外一个通道则是用来输出0.8V的电压来抬升电流采集的电压,也就是图1-9电流采集电路中的VCC_08V,放大倍数是7.02倍,采样电阻的电压经过放大之后直接接到stm32F4 的 ADC通道上,经过计算即可得到采样电阻的电流,也就是电机的电流。
(2)过流保护电路
采样电阻的电压经过放大之后除了连接到ADC通道同时也连接到板上的过流保护电路,核心器件是LMV331比较器,比较器的输出是接到半桥驱动器的 shutdown 引脚上,如果电压值超过2.17V,则会输出低电平,强制关断MOS管。
实际上这个比较器,添加了一个正反馈电阻,变成了滞回比较器,也叫施密特触发器。滞回比较器是一个具有迟滞回环传输特性的比较器。在反相输入单门限电压比较器的基础上引入正反馈网络,就组成了具有双门限值的反相输入迟滞比较器。由于反馈的作用这种比较器的门限电压是随输出电压的变化而变化的。如果输入信号 Uin在门限值附近有微小的干扰,则输出电压就会产生相应的抖动。在电路中引入正反馈可以克服这一缺点。滞回比较器的传输特性如图:
(3)半桥驱动
电机的驱动部分由2个半桥驱动电路组成,驱动芯片使用IR2104半桥驱动器,芯片带有ShutDown引脚,只要是低电平信号,即可切断输出,可作为硬件上的过流过压保护,同时也可以作为软件上的关闭控制功能,实现软关断。
IR2104 可以同时驱动两个NMOS 管,NMOS 管的导通条件是VGs大于一定的阈值电压VGs(th),IR2104的阈值电压是4V,当使能低端MOS 管导通的时候LO输出 VCC,低端 MOS 管很容易就可以满足导通条件,但是使能高端MOS 管导通的时候,Vs与 POWER 相同,VGs不能满足MOS 管的导通条件,所以即便使能了高端 MOS 管,也没办法完全导通,所以高端MOS 管使用二极管和电容组成自举电路,抬升电压,使得可以满足 MOS 管的导通条件。
IR2104内部集成了死区发生器,可输出一对带死区的互补的PWM信号同时驱动两个 MOSFET。输入输出时序如上图,只有在SD 是高电平的时候才能正常输出,所以正常使用的情况下需要把SD引脚拉高电平才行。芯片的控制信号高电平有效,shutdown引脚则是低电平有效。
半桥驱动器经过一个双通道的光耦连接到控制端口,也就是STM32 定时器的 PWM 输出端口。一个PWM通道可以控制一个半桥驱动器,所以控制一个H桥就需要两个 PWM 信号。两个通道都必须接到PWM 信号才能正常驱动H桥电路。
防止H桥Q1和Q2或者Q3和Q4同时导通,即每个桥的上半桥和下半桥绝对不能同时导通,需要加入死区时间,而IR2104半桥驱动芯片带有死区时间,如下图所示:
单极模式:电机电枢驱动电压极性是单一的
在PWM为高电平时:MOS管1和4都导通, MOS管2和3都截止,电流从电源正极,经过MOS管1,从左到右流过电机,然后经过MOS管4流入电源负极。
在PWM为低电平时:MOS管2和4都导通, MOS管1和3都截止,根据楞次定律,存在自感电动势,电流还是从左到右流过电机,经过MOS管4和MOS管2形成电流回路。
C、双极模式(所有 MOS 管连接 PWM 通道)
双极模式:电枢电压极性是正负交替的。
优点:能正反转运行,启动快,调速精度高,动态性能好,调速静差小,调速范围大,能加速,减速,刹车,倒转,能在负载超过设定速度时,提供反向力矩,能克服电机轴承的静态摩擦力,产生非常低的转速。
缺点:控制电路复杂。在工作期间,4个MOS管都处于工作状态,功率损耗大,电机容易发烫。
PWM1和PWM1N、PWM2和PWM2N是PWM互补通道。使用高级控制定时器通道和互补通道控制双极模式中,PWM1和PWM2周期相同,占空比相同,极性相反,使得对角线上的两个MOS管同时导通,同时关断。
记PWM1和PWM2周期为T,PWM1高电平时间为t1,那么:Umotor= (t1/T-(T-t1)/T) Udc,整理得: Umotor =(2a-1)Udc,其中:α为占空比。
结论:a>50%,Dpwm1>Dpwm2,电机正转 ;a<50%,Dpwm1<Dpwm2,电机反转;a=50%,Dpwm1=Dpwm2’电机停止;a=0,电机反转,速度最高; a=100%,电机正转,速度最高。
在PWM1为高电平时:MOS管1和4都导通, MOS管2和3都截止,电流从电源正极,经过MOS管1,从左到右流过电机,然后经过MOS管4流入电源负极。
在PWM为低电平时:MOS管2和3都导通, MOS管1和4都截止,虽然电机加了反向电压,但由于电机的负载电流较大,电流的方向仍然不改变,只不过电流幅值的下降速率叫单极模式要快,因此,电流波动较大。
4个 MOS 管都连接到stm32 的PWM输出通道,Q1、Q2 连接到一组互补通道,而 Q3、Q4 连接到另外一组互补通道。互补通道的意思是两个PWM 通道,同时输出相同频率,占空比互补,极性相反的PWM波形,如下图:
Q1 连接到 stm32 定时器的主输出通道,Q2则连接到相对的互补通道,在同一个周期内,输出的脉冲极性相反,不用担心两个MOS 管同时导通。同时Q3跟Q4 也连接到另一组互补通道。
在这种情况下,方向和速度值完全由两端的PWM占空比差值决定,如果Q1的占空比是20%,Q2的占空比就是80%,那么连接到这一端的电机电压是V*20%,如果 Q3 的占空比是80%,Q4的占空比是20%,那么这一端的电压是V*80%,最终电机两端的电压则是V(80%-20%),电流方向是Q3→电机→Q2,电机反转。正转则刚好相反。如果两端的电压相等,即占空比相同,则电机停止。
特别地,在使用H桥电路时,当同一侧的 Q1 和 Q2 同时导通时,电流将从电源先后经过 Q1 和 Q2,然后直接流到电源负极,在这个回路中除了三极管以外就没有其他负载(没有经过电机),这时电流可能会达到最大值,此时可能会烧毁三极管,同理,当 Q3 和 Q4 同时导通时,也会出现相同的状况。这样的情况肯定是不能发生的,但是我们写程序又是三分写代码七分在调试,这就难免会有写错代码将同一测得三极管导通的情况,为此我们就需要从硬件上来避免这个问题。下面电路图是改进后的驱动电路图。
我们来分析一下电信号的变化:在 ENABLE 脚接入高电平,在 IN1 脚接入高电平,在经过第一个非门后, AND1 的 2 脚就是低电平,此时与门 AND1 的输出(3 脚)就是低电平,所以 Q1 截止。而 AND2 的 1 脚和 2 脚都是高电平,所以 AND2 的 3 脚也是高电平,这样 Q2 就导通了。在IN2 接入低电平,同理分析可得, Q3 导通 Q4 截止。在 IN1 和 IN2 处分别接入低电平和高电平,则 Q1 和 Q4 导通, Q3 和 Q2 截止。当 IN1 和 IN2 都接入高电平或者低电平时都只会同时导通上面或者下面的两个三极管,不会出现同一侧的三极管同时导通的情况。
(1)、驱动芯片分析
通常在驱动电机的时候我们会选择集成 H 桥的 IC,因为 H 桥使用分立元件搭建比较麻烦,增加了硬件设计难度,当然如果集成 IC 无法满足我们的功率要求时,还是需要我们自己使用 MOS管、三极管等元件来搭建 H 桥电路,这样的分立元件搭建的 H 桥一般驱动能力都会比集成 IC 要高。当我们在选择集成 IC 时,我们需要考虑集成 IC 是否能满足电机的驱动电压要求,是否能承受电机工作时的电流等情况。
1)L298N 驱动芯片
L298N 是 ST 公司的产品,内部包含 4 通道逻辑驱动电路,是一种二相和四相电机的专门驱动芯片,即内含两个 H 桥的高电压大电流双桥式驱动器,接收标准的 TTL 逻辑电平信号,可驱动4.5V~46V、 2A 以下的电机,电流峰值输出可达 3A,其内部结构如下图所示。
其工作原理与上面的讲解的 H 桥原理一样,这里不再赘述。 L298N 引脚图如下图所示。
L298N 逻辑功能表。
IN3, IN4 的逻辑图与上表相同。由上表可知 ENA 为低电平时, INx 输入电平对电机控制不起作用,当 ENA 为高电平,输入电平为一高一低,电机正或反转。同为低电平电机停止,同为高电平电机停止。 L298N 的应用电路图将在后面硬件设计小节讲解。
2、直流有刷减速电机控制实现
(1)速度控制原理
脉冲宽度调制(Pulse width modulation, PWM)信号,即 PWM 是一种按一定的规则对各脉冲的宽度进行调制,既可改变电路输出电压的大小,也可改变输出频率。 PWM 通过一定的频率来改变通电和断电的时间,从而控制电路输出功率,在电机的控制周期中, 通电时间决定了它的转速。其中,通电时间/(通断时间 + 断电时间)= 占空比,即,高电平占整个周期的百分比,如下图所示:
上图中: T1 为高电平时间, T2 为低电平时间, T 是周期。
D(占空比) = T1/T*100%
设电机的速度为 V,最大速度为 Vmax。
则: V=Vmax*D
当占空比 D(0≤D≤1)的大小改变时,速度 V 也会改变,所以只要改变占空比就能达到控制的目的。
PWM频率一般在10KHz~20KHz之间。频率太低会导致电机转速过低,噪声较大。频率太高,会因为MOS管的开关损耗而降低系统的效率。
3、STM32驱动直流有刷电机示例
/******************************************************************************** 参数宏定义 *******************************************************************************/ //定时器周期 #define ADVANCED_TIME1_PERIOD 1000 //有刷电机的PWM频率一般为1kHz到20kHz,即1ms到0.05ms //预分频用来设置定时器的时钟频率 #define ADVANCED_TIME1_PRESCALER 168 //高级控制定时器时钟源TIMxCLK = HCLK=168MHz ,设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=168000000/168 = 1000000Hz =1us //重复计数寄存器值 #define ADVANCED_TIME1_REPETITION_COUNTER 1 //PWM初始脉宽,即设置CCR,无需减1 #define ADVANCED_TIME1_PWM_INIT_PULSE 0 //PWM初始占空比 #define ADVANCED_TIME1_PWM_INIT_DUTY_CYCLE (0.0f) //PWM最大占空比 #define ADVANCED_TIME1_PWM_MAX_DUTY_CYCLE (0.9f) //PWM最小占空比 #define ADVANCED_TIME1_PWM_MIN_DUTY_CYCLE (0.00f) //PWM最大比较值 #define ADVANCED_TIME1_PWM_MAX_COMPARE_VALUE (ADVANCED_TIME1_PERIOD*ADVANCED_TIME1_PWM_MAX_DUTY_CYCLE) //PWM最小比较值 #define ADVANCED_TIME1_PWM_MIN_COMPARE_VALUE (ADVANCED_TIME1_PERIOD*ADVANCED_TIME1_PWM_MIN_DUTY_CYCLE) /******************************************************************************** 宏定义 *******************************************************************************/ //高级定时器1 #define ADVANCED_TIME1 TIM1 //TEM1 #define ADVANCED_TIME1_CLK_ENABLE __TIM1_CLK_ENABLE //TEM时钟使能 #define ADVANCED_TIME1_CLK_DISABLE __TIM1_CLK_DISABLE //TEM时钟失能 #define ADVANCED_TIME1_IRQ TIM1_CC_IRQn //TIME2捕获/比较中断源 #define ADVANCED_TIME1_IRQHandler TIM1_CC_IRQHandler //TIME2捕获/比较中断函数 /******************************************************************************** 引脚宏定义 *******************************************************************************/ //高级定时器1 #define ADVANCED_TIME1_CH1_GPIO_PORT GPIOA #define ADVANCED_TIME1_CH1_GPIO_PIN GPIO_PIN_8 #define ADVANCED_TIME1_CH1_GPIO_CLK_ENABLE __GPIOA_CLK_ENABLE #define ADVANCED_TIME1_CH1_GPIO_AF GPIO_AF1_TIM1 #define ADVANCED_TIME1_CH1N_GPIO_PORT GPIOB #define ADVANCED_TIME1_CH1N_GPIO_PIN GPIO_PIN_13 #define ADVANCED_TIME1_CH1N_GPIO_CLK_ENABLE __GPIOB_CLK_ENABLE #define ADVANCED_TIME1_CH1N_GPIO_AF GPIO_AF1_TIM1 /******************************************************************************** 输入/捕获通道定义 *******************************************************************************/ #define ADVANCED_TIME1_CH1 TIM_CHANNEL_1 #define ADVANCED_TIME1_CH2 TIM_CHANNEL_2 #define ADVANCED_TIME1_CH3 TIM_CHANNEL_3 #define ADVANCED_TIME1_CH4 TIM_CHANNEL_4 /******************************************************************************** 结构体声明 *******************************************************************************/ typedef struct ADVANCED_TIME1_ParamTypeDef { __IO float pwm_duty_cycle; __IO int32_t pwm_compare_value; }ADVANCED_TIME1_ParamTypeDef; /******************************************************************************** 函数声明 *******************************************************************************/ void ADVANCED_TIME_ConfigInit(void); void ADVANCED_TIME_Open(uint8_t ADVANCED_Timex); void ADVANCED_TIME_Close(uint8_t ADVANCED_Timex); void ADVANCED_TIME1_SetMotorDirection(uint8_t Direction); void ADVANCED_TIME1_SetCompare(uint32_t Channel, uint32_t Compare); void ADVANCED_TIME1_SetBrushedMotorEnable(void); void ADVANCED_TIME1_SetMotorDisable(void); /******************************************************************************** 变量声明 *******************************************************************************/ extern TIM_HandleTypeDef ADVANCED_TIME1_HandleStruct; extern TIM_HandleTypeDef ADVANCED_TIME2_HandleStruct; extern ADVANCED_TIME1_ParamTypeDef ADVANCED_TIME1_ParamStruct; /** * @brief 高级定时器工作参数配置 * @param 无 * @retval 无 */ static void ADVANCED_TIME1_HandleConfig(void) { ADVANCED_TIME1_CLK_ENABLE(); ADVANCED_TIME1_HandleStruct.Instance = ADVANCED_TIME1; ADVANCED_TIME1_HandleStruct.Init.Period = ADVANCED_TIME1_PERIOD-1; ADVANCED_TIME1_HandleStruct.Init.Prescaler = ADVANCED_TIME1_PRESCALER-1; //高级控制定时器时钟源TIMxCLK = HCLK=168MHz ADVANCED_TIME1_HandleStruct.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; //采样时钟分频,设置定时器时钟 CK_INT 频率与死区发生器以及数字滤波器采样时钟频率分频比。 ADVANCED_TIME1_HandleStruct.Init.CounterMode=TIM_COUNTERMODE_UP; //计数方式 ADVANCED_TIME1_HandleStruct.Init.RepetitionCounter=ADVANCED_TIME1_REPETITION_COUNTER-1; //重复计数器:重复n次,即计数n+1次才生成一个中断 // HAL_TIM_Base_Init(&ADVANCED_TIME1_HandleStruct); HAL_TIM_PWM_Init(&ADVANCED_TIME1_HandleStruct); } /** * @brief 高级定时器PWM输出配置 * @param 无 * @retval 无 */ static void ADVANCED_TIME1_PwmOutputConfig(void) { GPIO_InitTypeDef GPIO_InitStruct; TIM_ClockConfigTypeDef TIM_ClockConfigStruct; // 定时器时钟 TIM_OC_InitTypeDef TIM_OCInitStructure; // 定时器通道比较输出 TIM_BreakDeadTimeConfigTypeDef TIM_BreakDeadTimeConfigStruct; // 定时器死区时间比较输出 ADVANCED_TIME1_CH1_GPIO_CLK_ENABLE(); ADVANCED_TIME1_CH1N_GPIO_CLK_ENABLE(); GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = ADVANCED_TIME1_CH1_GPIO_AF; GPIO_InitStruct.Pin = ADVANCED_TIME1_CH1_GPIO_PIN; HAL_GPIO_Init(ADVANCED_TIME1_CH1_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = ADVANCED_TIME1_CH1N_GPIO_PIN; HAL_GPIO_Init(ADVANCED_TIME1_CH1N_GPIO_PORT, &GPIO_InitStruct); //定时器时钟源配置 TIM_ClockConfigStruct.ClockSource = TIM_CLOCKSOURCE_INTERNAL; // 使用内部时钟源 HAL_TIM_ConfigClockSource(&ADVANCED_TIME1_HandleStruct, &TIM_ClockConfigStruct); //PWM模式配置 //所谓高电平有效就是指高电平的时候可以触发芯片的功能,在一个脉冲周期内,根据硬件的不同,可以是高电平有效也可以是低电平有效,在讨论占空比的时候应该是针对有效电平来讨论。 //所以有关占空比的说明都是指有效电平的占空比,如果是低电平有效的,则所说的占空比为60%,是指低电平所占时间为脉冲周期的60%。 TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1; //比较输出模式选择,总共有八种,常用的为 PWM1/PWM2。 TIM_OCInitStructure.Pulse = ADVANCED_TIME1_PWM_INIT_PULSE; //比较输出脉冲宽度,实际设定比较寄存器 CCR 的值,决定脉冲宽度,脉冲宽度是针对有效电平的(可以是高电平也可以是低电平)。可设置范围为0 至 65535。 TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_LOW; //比较输出极性,可选 OCx 为高电平有效或低电平有效。它决定着定时器通道有效电平。它设定 CCER 寄存器的 CCxP 位的值。 TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_LOW; //比较互补输出极性,可选 OCxN 为高电平有效或低电平有效。 TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_RESET; //空闲状态时(即停止PWM输出时)OCx通道输出电平设置,可选输出 1 或输出 0,即在空闲状态(BDTR_MOE 位为 0) 时,经过死区时间后定时器通道输出高电平或低电平。 TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET; //空闲状态时(即停止PWM输出时)OCxN互补通道输出电平设置,可选输出 1 或输出 0,即在空闲状态(BDTR_MOE 位为 0) 时,经过死区时间后定时器互补通道输出高电平或低电平,设定值必须与 OCIdleState 相反。 TIM_OCInitStructure.OCFastMode = TIM_OCFAST_DISABLE; //快速输出模式,加快输出比较对触发输入事件的响应,只能用于PWM模式 HAL_TIM_PWM_ConfigChannel(&ADVANCED_TIME1_HandleStruct,&TIM_OCInitStructure,ADVANCED_TIME1_CH1);//初始化通道1输出PWM //自动输出使能,断路、死区时间和锁定配置 TIM_BreakDeadTimeConfigStruct.OffStateRunMode = TIM_OSSR_ENABLE; //运行模式下的关闭状态选择,它设定 BDTR 寄存器 OSSR 位的值。 TIM_BreakDeadTimeConfigStruct.OffStateIDLEMode = TIM_OSSI_DISABLE; //空闲模式下的关闭状态选择,它设定 BDTR 寄存器 OSSI 位的值。 TIM_BreakDeadTimeConfigStruct.LockLevel = TIM_LOCKLEVEL_OFF; //锁定级别配置, BDTR 寄存器 LOCK[1:0] 位的值。 TIM_BreakDeadTimeConfigStruct.DeadTime = 0; //配置死区发生器,定义死区持续时间,可选设置范围为 0x0 至 0xFF。它设定BDTR 寄存器 DTG[7:0] 位的值。 TIM_BreakDeadTimeConfigStruct.BreakState = TIM_BREAK_DISABLE; //断路输入功能选择,可选使能或禁止。它设定 BDTR 寄存器 BKE 位的值。 TIM_BreakDeadTimeConfigStruct.BreakPolarity = TIM_BREAKPOLARITY_LOW; //断路输入通道 BRK 极性选择,可选高电平有效或低电平有效。它设定 BDTR寄存器 BKP 位的值。 TIM_BreakDeadTimeConfigStruct.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; //自动输出使能,可选使能或禁止,它设定 BDTR 寄存器 AOE 位的值。 HAL_TIMEx_ConfigBreakDeadTime(&ADVANCED_TIME1_HandleStruct, &TIM_BreakDeadTimeConfigStruct); HAL_TIM_Base_Start(&ADVANCED_TIME1_HandleStruct);//定时器启动运行 HAL_TIM_PWM_Stop(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); HAL_TIMEx_PWMN_Stop(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); } /** * @brief 高级定时器初始化 * @param 无 * @retval 无 */ void ADVANCED_TIME_ConfigInit(void) { ADVANCED_TIME_NvicConfig(); ADVANCED_TIME1_HandleConfig(); ADVANCED_TIME1_PwmOutputConfig(); } /** * @brief 基本定时器硬件初始化配置,该函数被HAL库内部调用. * @param 无 * @retval 无 */ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) { //BDCMOTOR相关GPIO初始化配置 if (htim == &ADVANCED_TIME1_HandleStruct) { //引脚端口时钟使能 ADVANCED_TIME1_CH1_GPIO_CLK_ENABLE(); ADVANCED_TIME1_CH1N_GPIO_CLK_ENABLE(); } } /** * @brief 基本定时器硬件反初始化配置,该函数被HAL库内部调用 * @param htim_base:基本定时器句柄类型指针 * @retval 无 */ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim) { if (htim == &ADVANCED_TIME1_HandleStruct) { ADVANCED_TIME1_CLK_DISABLE();//基本定时器外设时钟禁用 HAL_GPIO_DeInit(ADVANCED_TIME1_CH1_GPIO_PORT,ADVANCED_TIME1_CH1_GPIO_PIN); HAL_GPIO_DeInit(ADVANCED_TIME1_CH1N_GPIO_PORT,ADVANCED_TIME1_CH1N_GPIO_PIN); } } /** * @brief 开启通用定时器 * @param ADVANCED_Timex:要开启的通用定时器 * @retval 无 */ void ADVANCED_TIME_Open(uint8_t ADVANCED_Timex) { if (ADVANCED_Timex == 1) { __HAL_TIM_CLEAR_FLAG(&ADVANCED_TIME1_HandleStruct, TIM_FLAG_UPDATE); __HAL_TIM_CLEAR_IT(&ADVANCED_TIME1_HandleStruct, TIM_IT_UPDATE); HAL_TIM_Base_Start_IT(&ADVANCED_TIME1_HandleStruct); } else if (ADVANCED_Timex == 2) { __HAL_TIM_CLEAR_FLAG(&ADVANCED_TIME2_HandleStruct, TIM_FLAG_UPDATE); __HAL_TIM_CLEAR_IT(&ADVANCED_TIME2_HandleStruct, TIM_IT_UPDATE); HAL_TIM_Base_Start_IT(&ADVANCED_TIME2_HandleStruct); } } /** * @brief 关闭通用定时器 * @param ADVANCED_Timex:要关闭的通用定时器 * @retval 无 */ void ADVANCED_TIME_Close(uint8_t ADVANCED_Timex) { if (ADVANCED_Timex == 1) { HAL_TIM_Base_Stop_IT(&ADVANCED_TIME1_HandleStruct); // 关闭计数器 } else if (ADVANCED_Timex == 2) { HAL_TIM_Base_Stop_IT(&ADVANCED_TIME2_HandleStruct); } } #if 0 /** * @brief 设置有刷电机转动方向 * @param Direction:电机转动方向 * @retval 无 */ void ADVANCED_TIME1_SetMotorDirection(uint8_t Direction) { GPIO_InitTypeDef GPIO_InitStruct; ADVANCED_TIME1_CH1_GPIO_CLK_ENABLE(); ADVANCED_TIME1_CH1N_GPIO_CLK_ENABLE(); HAL_TIM_PWM_Stop(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); HAL_TIMEx_PWMN_Stop(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); // 停止输出 if (Direction) { /* 设置PWM输出引脚*/ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = ADVANCED_TIME1_CH1_GPIO_AF; GPIO_InitStruct.Pin = ADVANCED_TIME1_CH1_GPIO_PIN; HAL_GPIO_Init(ADVANCED_TIME1_CH1_GPIO_PORT, &GPIO_InitStruct); HAL_TIM_PWM_Start(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); /* 普通IO输出控制 */ GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = 0; GPIO_InitStruct.Pin = ADVANCED_TIME1_CH1N_GPIO_PIN; HAL_GPIO_Init(ADVANCED_TIME1_CH1N_GPIO_PORT, &GPIO_InitStruct); HAL_GPIO_WritePin(ADVANCED_TIME1_CH1N_GPIO_PORT,ADVANCED_TIME1_CH1N_GPIO_PIN,GPIO_PIN_SET); } else { /* 设置PWM输出引脚*/ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = ADVANCED_TIME1_CH1N_GPIO_AF; GPIO_InitStruct.Pin = ADVANCED_TIME1_CH1N_GPIO_PIN; HAL_GPIO_Init(ADVANCED_TIME1_CH1N_GPIO_PORT, &GPIO_InitStruct); HAL_TIMEx_PWMN_Start(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); /* 普通IO输出控制 */ GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = 0; GPIO_InitStruct.Pin = ADVANCED_TIME1_CH1_GPIO_PIN; HAL_GPIO_Init(ADVANCED_TIME1_CH1_GPIO_PORT, &GPIO_InitStruct); HAL_GPIO_WritePin(ADVANCED_TIME1_CH1_GPIO_PORT,ADVANCED_TIME1_CH1_GPIO_PIN,GPIO_PIN_SET); } } #else /** * @brief 设置有刷电机转动方向 * @param Direction:电机转动方向 * @retval 无 */ void ADVANCED_TIME1_SetMotorDirection(uint8_t Direction) { #if 1 //pwm单端输出 if (!Direction) { HAL_TIM_PWM_Start(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); HAL_TIMEx_PWMN_Stop(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); } else { HAL_TIM_PWM_Stop(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); HAL_TIMEx_PWMN_Start(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); } #else //pwm互补输出 HAL_TIM_PWM_Start(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); HAL_TIMEx_PWMN_Start(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); #endif } #endif /** * @brief 设置有刷电机转动使能 * @param 无 * @retval 无 */ void ADVANCED_TIME1_SetBrushedMotorEnable(void) { //如果 OC 和 OCN 输出的相应使能位( TIMx_CCER 寄存器中的 CCxE 和 CCxNE 位)均置 1,则使能 OC 和 OCN 输出。 __HAL_TIM_MOE_ENABLE(&ADVANCED_TIME1_HandleStruct); } /** * @brief 禁止有刷电机转动 * @param 无 * @retval 无 */ void ADVANCED_TIME1_SetMotorDisable(void) { __HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(&ADVANCED_TIME1_HandleStruct);//OC 和 OCN 输出禁止或被强制为空闲状态 } /** * @brief 设置ADVANCED_TIME1通道的比较值 * @param channel:通道(1,2,3,4) compare:比较值,即设置CCR,无需减1 * @retval 无 */ void ADVANCED_TIME1_SetCompare(uint32_t Channel, uint32_t Compare) { switch (Channel) { case 1: __HAL_TIM_SetCompare(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1,Compare); break; case 2: __HAL_TIM_SetCompare(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH2,Compare); break; case 3: __HAL_TIM_SetCompare(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH3,Compare); break; case 4: __HAL_TIM_SetCompare(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH4,Compare); break; default: break; } } /** * @brief 高级定时器1捕获中断启动 * @param channel:通道(1,2,3,4) * @retval 无 */ void ADVANCED_TIME1_SetIcStart(uint32_t Channel) { switch (Channel) { case 1: HAL_TIM_IC_Start_IT(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); break; case 2: HAL_TIM_IC_Start_IT(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH2); break; case 3: HAL_TIM_IC_Start_IT(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH3); break; case 4: HAL_TIM_IC_Start_IT(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH4); break; default: break; } } /** * @brief 高级定时器1捕获中断停止 * @param channel:通道(1,2,3,4) * @retval 无 */ void ADVANCED_TIME1_SetIcStop(uint32_t Channel) { switch (Channel) { case 1: HAL_TIM_IC_Stop_IT(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH1); break; case 2: HAL_TIM_IC_Stop_IT(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH2); break; case 3: HAL_TIM_IC_Stop_IT(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH3); break; case 4: HAL_TIM_IC_Stop_IT(&ADVANCED_TIME1_HandleStruct,ADVANCED_TIME1_CH4); break; default: break; } }
定时器配置的一些注意事项:
3.1、定时器基本配置
(1)PWM频率
有刷电机的PWM频率设置需综合考虑电机特性、应用场景及控制需求,典型范围在 1 kHz 到 20 kHz 之间,具体选择依据如下:
关键选择因素:
(1) 电机电感与电流纹波
低电感电机(如小型有刷电机):
需更高PWM频率(如10kHz以上)以抑制电流纹波,避免转矩波动或发热。
公式:电流纹波 ΔI ≈ V<sub>bus</sub> / (L × f<sub>PWM</sub>),频率越高,纹波越小。
(2) 开关损耗与效率
大功率电机:优先选择低频(1kHz~5kHz),降低MOSFET/BJT的开关损耗(P<sub>loss</sub> ∝ f<sub>PWM</sub>)。
(3) 听觉噪音
人耳敏感范围:20Hz~20kHz。
若频率 <20kHz(如8kHz),可能听到高频啸叫;>20kHz(超声波)则无噪音,但需硬件支持。
(4) 控制响应速度
高频PWM(如20kHz):
更适合闭环控制(如PID),因电流环带宽可更高,动态响应更快。
(2)计数模式
在有刷电机控制中,定时器通常选择递增计数模式(Edge-Aligned Upcounting)而非递减或中心对齐模式,主要基于以下技术原因和实际需求:
(1)递增模式的天然优势
1) 硬件设计简化
占空比计算直观:递增模式下,占空比直接由 CCR/ARR 决定,无需像中心对齐模式需计算 (ARR-CCR)/ARR。
示例:设定 ARR=999,CCR=300 → 占空比=30%(无需二次换算)
2) 实时性要求
响应速度更快:有刷电机通常用于低成本、高动态响应场景(如玩具、电动工具),递增模式的PWM更新发生在计数器归零时,比中心对齐模式(需等待ARR→0→ARR)延迟更低。
3) 资源占用少
减少计算开销:递减模式或中心对齐模式需要处理双向计数逻辑,对低端MCU(如8位单片机)会增加软件复杂度。
(2)为何不选择其他模式?
1) 递减模式(Downcounting)的局限性
应用场景狭窄:递减模式主要用于特定场合(如生成反向PWM),但有刷电机驱动通常只需单向调速,递增模式已足够。
调试复杂度高:递减计数的占空比需转换为 (ARR-CCR)/ARR,增加调试难度。
2) 中心对齐模式(Center-Aligned)的不必要性
谐波问题不突出:有刷电机的换向噪声远大于PWM谐波,中心对齐模式对EMI改善有限,反而牺牲了实时性。
电流采样非必需:有刷电机通常采用开环或简单速度闭环,无需像FOC那样在计数器过零点同步采样电流。
(3)例外场景分析
尽管递增模式是主流选择,以下情况可能需其他模式: