完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本文介绍如何使用STM32F103单片机,通过官方固件库,设置高级定时器TIM1输出嵌入死区的互补PWM,来驱动直流电机的程序设计与电路设计。硬件电路采用IR2110S芯片作为mos管的驱动,驱动IRF840组成的H桥。IR2110S芯片使用中,有一个比较难理解的点——自举电容,本文对其原理也有涉及。
原理图文件 上图是系统的简化原理图,左侧是单片机。中间是IR2110S芯片,为了方便讲解,把芯片内部结构列出一些。右侧是MOS管组成的H桥。其中M是直流电机,有正反转。其中VCC是15V,MOTOR_VCC是24V,电压可以改变,最大不超过500V。单片机一般是3.3V或5V,无法直接驱动电机。可以借助H桥来实现对直流电机的控制。 H桥由于形似H得名。 VT1,VT4导通,电机正转 VT2,VT3导通,电机翻转 VT1,VT3导通,短路,板子烧坏 VT2,VT4,导通短路。 所以,驱动电机的问题就变成了MOS管导通的问题。 实际电路中我选用了IRF840,这是N沟道的MOS管。N-MOS导通的条件:VGS大于一定值 对于IRF840,VGS>10V 所以MOS管导通的问题就变成了VGS>10V的问题 如果,VT1与VT3都独立配置一个电源,独立配置一套驱动,导通问题就变得简单了。但是,电路设计会变得复杂。 我们使用1个驱动芯片IR2110S,一路驱动电源。 简单介绍下IR2110S芯片 IR2110是独立一桥臂双通道,栅极驱动,高压,高速单片机专用功率器件集成驱动电路。2片IR2110就能构成H桥驱动电路。 IR2110S是3.3V版本 感兴趣的可以自己来做个阅读理解。 简单来说,IR2110是个3.3V控制10-20V的一个驱动。开关速度也很快,120ns 内部结构 右上角的两个MOS管,中间是非门连接,不会同时导通。 中间一系列怎么变化,我也不是很清楚。 HIN是1,VM1导通,VM2截止。VB与HO连在一起 HIN是0,VM1截止,VM2导通。VS与HO连在一起 LIN是1,VM3导通,VM4截止。VCC与LO连在一起 LIN是0,VM3截止,VM4导通。LO与COM连在一起 先看下桥臂。左侧下桥臂导通,很简单: LIN为高,VM3导通,VCC接在LO上(暂时忽略二极管压降),VGS= VCC,导通 上桥臂导通的情况,先假设没有电容。 VM1导通时,VCC接在HO上,为G极提供了接近15V的电压。但是,VS的电压是多少?不知道。 如果,VT3导通,VS就是0,VT1也导通了,烧坏。 如果VT4导通,VS通过电机接地(电机内部可以先等效为电阻)。但是VT1导通以后,VS接近24V,HO只有15V,VT1又截止了。电机还是不能工作。 我们面临的问题是,上桥臂没有地。怎么办? 这个时候,就需要自举电容。 VT4导通,VS接地。电容一端是地,一端是15V,所以VCC通过D给C充电。 又因为VM1导通,所以C横跨在GS上。所以,C可以作为电压源,为GS供电。这是一个悬浮的电压源。 VT1导通后,VS接近24V,不再是地。所以VCC15无法为G提供足够的电压。 自举电容可以放电维持VT1工作,电容存有15V的电,可以保持MOS管的导通。由于电容两极的压差不能突变,而电容下边变成了对地24V(暂不考虑MOS管压降),所以这一瞬间,电容上边的电压是对地39V。这时,VCC无法为电容充电。由于二极管的存在,电容的电不会倒灌给VCC。 电容电量又是有限的,放电会导致电容的电压降低。等到两级压差不到10V的时候,VT1又不工作了。并且,此时IR2110芯片内部的欠压检测逻辑就会工作,把HO拉到VS,让VGS=0。 所以,自举电容电压小于10V之前,要充电。如果HIN一直是高电平,电容就没有充电的机会,等到自举电容的的电压跌落到某个阈值以下,HO就变为低电平。 此时可以关掉VT1,也就是断开VM1,VB与HO断开,不论是VCC还是电容都不再为G极提供电源。如果此时打开VT3,让VS接地,则电容一边是高电平,一边是低电平,开始充电。然后再断开VT3,打开VM1,VT4保持不变,让电容放电维持VT1导通,就可以循环往复,保持电机运行。 即,VT1的导通要依靠电容放电来维持。HIN不能为持续的高电平,占空比也不能达到100,或低频的PWM(频率低,一个周期内放电时间长)。必须是高频的PWM,保证自举电容有周期性的,足够的充电时间,才能维持较高的悬浮电源电压。 除此之外,还要注意死区问题,由于绝对不可以把同侧桥臂的上下半桥同时打开,而IR2110S,MOS管与电机切换状态都存在延时,导致从程序命令某半桥关断,到实际关断,有一段时间的延迟。例如,在延迟期间,上半桥正在关闭,下半桥暂时还不能打开,直到上半桥完全关闭,下半桥才能打开。中间等待的这段时间,就是死区。死区时间与硬件密切相关。笔者手上就有两个不同型号的电机,一个在3us的死区时可以工作,另一个则不可以。 所以,写程序要注意到上下桥不能同时导通,高频,不能是100的占空比,以及死区这几个问题。接下来尝试用STM32的高级定时器,输出嵌入死区的互补PWM。 以下是定时器1的初始化代码,使用两个通道输出PWM,一个周期是100us,频率是10KHz,3us的死区时间。默认通道一的占空比是50%,通道2的占空比是0%,让电机以47%(占空比减去死区)的速度正转。 通道2输出占空比是0,可以让右侧上半桥总是截止,下半桥总是导通。下半桥没有自举电容。如此一来,只需要左侧上半桥导通,就可以让电机正转。控制左侧桥臂的占空比,就能控制电机的占空比。 定时器1的通道1引脚是PA8PB13,通道2的输出引脚是PA9PB14。 void PWM_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_BaseInitStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; NVIC_InitTypeDef NVIC_InitStructure; //开启TIM和相应端口时钟 //启动GPIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); //启动AFIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //启动TIM1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //GPIO做相应设置,为AF输出 //PA8,PB13一组互补输出 A9,PB14一组互补输出 //PA.8/9口设置为TIM1的OC1输出口 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //PB.13/14口设置为TIM1_CH1N和TIM1_CH2N输出口 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOA, GPIO_Pin_8 | GPIO_Pin_9); GPIO_SetBits(GPIOB, GPIO_Pin_13 | GPIO_Pin_14); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 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); //TIM1基本计数器设置(设置PWM频率)10KHz TIM_BaseInitStructure.TIM_Period = 100-1; //10khz 好计算。按照1%的精确度,理论最大72000/100 = 720KHz TIM_BaseInitStructure.TIM_Prescaler = 72-1; TIM_BaseInitStructure.TIM_ClockDivision = 0; TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数 TIM_BaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1, &TIM_BaseInitStructure); //启用ARR的影子寄存器(直到产生更新事件才更改设置) TIM_ARRPreloadConfig(TIM1, ENABLE); //TIM1_OC1模块设置(设置1通道占空比) TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//TIM脉冲宽度调制模式1 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出通道使能 TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;//互补输出 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;//TIM输出比较极性高 //TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; TIM_OCInitStructure.TIM_Pulse = 50;//待装入捕获比较寄存器的脉冲值 TIM_OC1Init(TIM1, &TIM_OCInitStructure); //启用CCR1寄存器的影子寄存器(直到产生更新事件才更改设置) TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //TIM1_OC2模块设置(设置2通道占空比) TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; TIM_OC2Init(TIM1, &TIM_OCInitStructure); //启用CCR2寄存器的影子寄存器(直到产生更新事件才更改设置) TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable); //OCx输出信号与参考信号相同,只是它的上升沿相对参考信号的上升沿有一个延迟 //OCxN输出信号与参考信号相同,只是它的上升沿相对参考信号的下降沿有一个延迟 //死区设置 TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable; TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_2; //bit7~5 = 111,则deadtime = (32 + (bit4~bit0)* 16*1/fosc)ns = (32+31)*16*1/72000000 = 14us TIM_BDTRInitStructure.TIM_DeadTime = 0xab; //这里调整死区大小0-0xff3us 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); //TIM1_OC通道输出PWM TIM_CtrlPWMOutputs(TIM1, ENABLE); //TIM1开启 TIM_Cmd(TIM1, ENABLE); } 从上图中,可以清楚地看到,单片机输出的上、下桥臂控制信号,不存在同时为高电平的时候,也就是同侧上下桥臂不会同时导通。切换状态时,某半桥臂的控制信号拉低3us以后,另半桥臂的控制信号才能拉高,这就是所谓的带死区的互补PWM。 上图是输出的电机控制电压。可以看出周期是100us,高电平持续时间大约一半。 初始化之后,在程序运行时,可以调用 TIM_SetAutoreload(TIM1,xx); 1 来设置自动重装值。在初始化的时候把此值设置为了100,如果改为80,效果如下: 可以看出,一个周期变成了80us。 也可以使用函数 TIM_SetCompare1(TIM1,xx); TIM_SetCompare2(TIM1,xx); 1 2 来分别为通道1与通道2设置比较值。例如,把比较值设置为80,而自动重装值还是100,那么占空比就是80%了。 下边是用按键控制电机的一个小demo。实现了按键1启停,按键2切换正反转,按键34增减转速的功能。 int main(void) { static u8 motorValue = 50,oldvalue = 50; static u8 startStop = 1,dir = 1;//startStop = 1启动 =0停止 dir =1 正转 volatile u8 key = 0; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 LED_Init(); KEY_Init(); delay_init(); PWM_Configuration(); Motor_IO_Init(); LED1 = LED_ON; while(1) { key = KEY_Scan(0); if(key) { switch(key) { case KEY1_PRES: startStop = !startStop; break; case KEY2_PRES: dir = !dir; break; case KEY3_PRES: motorValue += 5; break; case KEY4_PRES: motorValue -= 5; break; default: break; } if(motorValue>249)//<0 motorValue = 0; else if(motorValue >94) motorValue = 94; if (startStop) { if(dir) { TIM_SetCompare2(TIM1,0); delay_ms(500); TIM_SetCompare1(TIM1,motorValue); } else { TIM_SetCompare1(TIM1,0); delay_ms(500); TIM_SetCompare2(TIM1,motorValue); } } else { TIM_SetCompare1(TIM1,0); TIM_SetCompare2(TIM1,0); } } } } //S1 启动&停止 S2翻转 S3+5 S4-5 |
|
|
|
只有小组成员才能发言,加入小组>>
2384 浏览 0 评论
8888 浏览 4 评论
36462 浏览 18 评论
4980 浏览 0 评论
24278 浏览 34 评论
1367浏览 2评论
1616浏览 1评论
2006浏览 1评论
1431浏览 0评论
1863浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-19 20:33 , Processed in 1.319390 second(s), Total 97, Slave 81 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号