赞
踩
笔者从开始接触嵌入式单片机开始,就和驱动电机相伴而走。从最开始的直接买L298N驱动模块直接驱动直流电机,到现在自己设计PCB电路板驱动直流电机,可以说是和电机驱动共同成长了。现在笔者将这一过程的收获记录下来,希望对大家有所帮助。
不用详细了解L298N芯片和IR2001S的芯片引脚,但是要知道这两个芯片是干嘛的。这两个芯片的相同功能就是把单片机输出的3.3VPWM波变成你给这两个芯片供电的电压PWM波。比如你给芯片供电12V,那么输出的就是12V的PWM波,占空比和单片机输出的PWM波占空比一致。
改变电机的电压可以改变电机的转速,改变电机的正负极可以改变电机的转向。改变电机的驱动电压通过改变PWM波的占空比实现,比如给芯片供电12V,PWM的占空比是60%,那么就相当于给电机供电12V*60% = 7.2V。供电电压乘以占空比就是电机的供电电压。需要注意的是,这里的占空比指的是高电平时间和周期的比值。
改变电机的转向(改变电机的正负极),这个操作很简单,会在下面介绍。
STM32输出普通PWM笔者这里不再赘述,这个驱动过程十分简单,网上随便一搜索就有一大堆的例子。
L298N可以买到别人设计好的模块,在淘宝等店铺直接搜索L298N电机驱动模块就可以看到各种各样的产品。这些产品的驱动方式大同小异,笔者下面将选取一种介绍。
如上图所示,怎么使用这个这个模块呢?听笔者一一介绍。首先,这个模块需要供电,从图中可以看到供电部分由12V供电、供电GND、5V供电、和板载5V使能的那个跳线帽组成。如果板载5V使能那个跳线帽安上,那么就不需要给驱动模块单独5V供电,只需要12V供电就行了。一般来说会使用这种方式。另一种方式就是12V、5V都给供电,然后把板载5V使能的那个跳线帽去掉。
然后看通道A使能、逻辑输入、通道B使能的那一部分。左边的3排控制左边的输出A通道,右边的3排控制右边的输出B通道。笔者以左边三排为例进行讲解,右边三排和左边是对称的。先看通道A使能的那个跳线帽,使用时将那个跳线帽拔掉,然后把单片机的PWM输出引脚接到靠近板边缘的排针上面。需要注意的是,最好通过光耦或者其他方式将单片机引脚和使能排针做一下隔离。如果不做隔离,尽量不要使用外力让电机转动,不然电机会发电,有可能烧坏单片机等电路。逻辑输入靠近通道A使能的那两个排针是用来控制通道A的电压正负的,给这两个排针1 0,通道A会输出一个电压,给这两个排针0 1,通道A会输出和1 0 相反的电压,从而让电机反转。这两个逻辑控制可以直接接到单片机的IO口,用IO口控制。当然,如果不需要正反转,可以直接给固定的电压就好。
L298N到这里介绍结束,总结一下。给供12V的电压,把单片机的PWM输出引脚接上,再把逻辑输入接上,就可以控制电机的转速和正反转了。
笔者使用的单片机型号是STM32F103C8T6,下面将介绍基于这款单片机高级定时器一的带死区的互补PWM怎么配置的。事实上,由于CMSIS协议的存在,这个配置只需做少量修改或者无需修改就可以使用在Cotex-M内核的其他单片机上。
代码如下:
void TIM1_PWM_DeadtimeInit(u16 arr,u16 psc,u16 deadtime)
{
GPIO_InitTypeDef GPIO_InitSturcture;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//时钟配置
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE );
RCC_APB2PeriphClockCmd( RCC_APB2Periph_TIM1, ENABLE );
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//TIM1 PWM CH1->PA8 CH2->PA9 CH1N->PB13 CH2N->PB14,PWM管脚映射
//与PWM管脚初始化
GPIO_InitSturcture.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitSturcture.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitSturcture.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitSturcture );
GPIO_InitSturcture.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
GPIO_InitSturcture.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitSturcture.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOB, &GPIO_InitSturcture );
GPIO_SetBits(GPIOA, GPIO_Pin_8 | GPIO_Pin_9);
GPIO_SetBits(GPIOB, GPIO_Pin_13 | GPIO_Pin_14);
//关于定时器中断的配置,有的资料上需要配置,有的资料上不需要
//配置,笔者配置和不配置的都试了,都可以使用,这里还是配置上
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//关于定时器重装载值和分频系数的配置,决定了PWM的频率
//timer base set
TIM_TimeBaseInitStructure.TIM_Period = arr;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0x00;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00;
TIM_TimeBaseInit( TIM1, &TIM_TimeBaseInitStructure );
TIM_ARRPreloadConfig(TIM1, ENABLE);
//OC1 Mode set 高级定时器互补输出的设置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_Pulse = 50;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
TIM_OC1Init( TIM1, &TIM_OCInitStructure );
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
//OC2 Mode set
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC2Init( TIM1, &TIM_OCInitStructure );
TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
//DeadTime Set 死区时间的设置
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_2;
TIM_BDTRInitStructure.TIM_DeadTime = deadtime;
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig( TIM1, &TIM_BDTRInitStructure );
TIM_CtrlPWMOutputs( TIM1, ENABLE );
TIM_Cmd( TIM1, ENABLE );
}
代码如上图所示,和常规固件库编程思路一样:对时钟进行配置、对管脚进行初始化、对定时器进行初始化、对PWM进行初始化、对死区进行配置。
这里有几点需要注意。首先是高级互补输出的设置的第一行,那里有几种不同的模式,笔者选择的是PWM模式一。如果感兴趣,你可以试一试其他的模式。
第二点是死区时间的设置。死区时间的计算比较复杂,感兴趣的请移步以下链接,是CSDN的另一个博主写的。
链接: STM32F1死区时间配置详解
第三点是这个函数怎么用。需要首先在主函数里进行初始化。
TIM1_PWM_DeadtimeInit(36000-1,0,0XAC);
这是笔者使用的。函数第一个形参是PWM重装载值,第二个形参是PWM分频系数,第三个是死区时间的设置。0XAC表示死区时间是3us。笔者使用的配置的意思是:不分频、计时器到(36000-1)会重新开始计数、由分频系数和重装载值可算出PWM频率是2kHz。
第四点是如何更改PWM的占空比,和普通PWM更改的方式一样。使用
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);函数更改比较值就可以了。
由于该互补PWM是用来驱动IR2110S的,笔者已经在IR2110S上进行了实验,死区时间和频率都没有任何问题。
接下来是IR2110S如何使用的问题了。IR2110S目前集成好的模块产品并不是很多,笔者所知野火有一款直流无刷电机驱动板,里面的H桥电路是用IR2110S驱动的。一般来说,使用IR2110S都需要自己设计PCB。当然,不需要知道原理的情况下IR2110S的使用十分简单。
如上图所示,这是使用IR2100S的一种情况。从左侧看,直接将单片机的互补PWM引脚接到V+和V-,然后给芯片供电,按照电路连接就可以驱动芯片在V_OUT端输出高电压(POWER)的PWM波形。
需要注意三点。第一点是SD端口是控制芯片使能的,悬空或者接0电位芯片正常工作,接3.3V或5V电平芯片停止工作。所以在使用中一般直接悬空就可以。第二点是图上的12V电压,可以根据需要选择,只要比场效应管的导通电压Vgs高一些就可以。比如我选择的场效应管的导通电压是10V,那我就选择了12V的电压。
第三点是在使用IR2110S驱动电机的时候,如果想要输出POWER峰值的PWM波,必须要给V+和V-这两个引脚输出互补带死区的PWM波,频率还要合适。这是由于自举电路的存在,如果频率不对或者不是互补的PWM波,都会导致自举电路变成不举电路。举不起来也就无法完成驱动。
那么IR2110S怎么控制电机的正反转呢。其实只需要两个这样的电路,分别把输出接口接到电机的两端就可以了。驱动其中一个电路,让另一个电路和地连接的场效应管一直导通,是一个方向的电压。反过来驱动另外一个电路是反方向的电压。这样就实现了H桥电路。
IR2110S具有集成度高,响应速度快等诸多优势。最重要的是,驱动N多个芯片只需要一路电源,大大的减少了设计的难度。这一切都要归功于自举电路。下面将介绍自举电路的原理。
如上图,V-为高点平,V+为低电平,此时Q4导通,Q3截止。
Q4导通后,请看我右边画的线,此时电容会通过该回路充电。经过一小段时间后,电容会充电到12V。
如上图,在电容充满12V电后,紧接着V+导通,V-截止。
在变换的瞬间,Q4截止,此时Q3导通。当POWER电压到达V_OUT的一刻,由于POWER一般大于12V,所以Q3马上截止。
请注意,精彩的部分来了。由于Q4截止后,电容两端电压不能突变,所以电容C11上方还保留着12V的电压。由于POWER到达了V_OUT一瞬间,所以在这一瞬间,C11上的电压是POWER+12V。此时这个POWER+12V在IR2110S内部电路(就是两个场效应管)作用下会给到HO端,此时Q3在这一瞬间又被导通,直到C11放电完成。
这就是一次循环过程,接下来的充电放电就是重复上述的两个过程。所以这就是为什么要互补的PWM波,就是为了给自举电路的电容充电和放电的!!!
请多多关注,接下来笔者可能更新一波如何使用PID算法做简单的自动驾驶。
笔者已经尽可能的理清逻辑,但是难免会有些地方不容易理解,所以欢迎大家多多留言提问和指教呀。技术问题,有问必回!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。