【单片机】DIY无刷电机驱动器 2

2024-04-20 21:48

本文主要是介绍【单片机】DIY无刷电机驱动器 2,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

接着上篇,先解释一下为什么会出现“共振”现象,如下图:

假如A1的占空比为25%,B2的占空比为10%,C2的占空比也为10%,我一开始想象的A1和B1之间的信号强度应该是:

25% x 10% = 2.5%

但是实际远不是这么回事,假如B2的高电平时间范围正好都落在了A1的高电平时间范围中,那么A1到B2的信号的实际占空比就是B2信号的占空比10%。但是如果 B2的高电平时间范围没有完全都落在了A1的高电平时间范围中或者完全没有落在A1的高电平时间范围中的话,A1到B2的有效信号宽度是A1和B2的信号中高电平重叠部分的宽度。比如C2的信号,高电平信号完全落在了A1信号高电平的外部,也就是A1到C2之间根本没有重叠的部分,不会有电流产生,即使A1和C2的占空都比大于0。

为了解决这个问题,我的做法是将所有PWM通道初始化之后先不要使能定时器,而是在初始化完所有定时器之后再统一进行定时器的使能,这样几个定时器的使能时间差在几个us的级别,甚至更小(CPU为48MHz主频,执行这几条指令耗时很低,而且将全局中断关闭,防止有中断打断该过程),而PWM信号的频率一般是20KHz,周期为50us,这样可以让PWM信号的初相尽可能一样,这样就能让PWM信号的重叠区域最大,示例程序如下:

    ......TIM1_PWM_Init(255 - 1,10);    //48MHz / 10 / 255 = 18.8KHzTIM3_PWM_Init(255 - 1,10);TIM14_PWM_Init(255 - 1,10);......__disable_irq();	//关闭全局中断int tmp1,tmp2,tmp3;tmp1 = TIM1->CR1  | TIM_CR1_CEN;tmp2 = TIM3->CR1  | TIM_CR1_CEN;tmp3 = TIM14->CR1 | TIM_CR1_CEN;TIM1->CR1  = tmp1;TIM3->CR1  = tmp2;TIM14->CR1 = tmp3;__enable_irq();    //打开全局中断......

测试一下A1、B1、C1的波形相位问题:

可以看出来A1和B1的相位差0.25u(因为我的逻辑分析仪的采样率是4MHz,最小分辨率0.25us,所以实际的大小可能是小于0.25us的),已经很小了,但是B1和C1没有相位差,因为B1和C1都是TIM3输出的PWM,肯定没有误差。可以猜测B1和C2差不多也误差0.25us以下,属于可接受范围了,可以使用PWM信号来控制电流大小。实在是缺示波器

下面为了使得PWM信号派上用场,这里对三相波形的细分。三相驱动信号原图为:

我们将电流的瞬变改成渐变,如下图的紫色、蓝色、绿色三根:

为了提高驱动电流,将t0到t5这6个阶段拆分成t0_1、t0_2、t1_1、t1_2、t2_1、t2_2、t3_1、t3_2、t4_1、t4_2、t5_1、t5_2这12个阶段,每一个单独的阶段中只有一相信号处于渐变状态,其余两相处于满状态(占空比100%),这样就提高了总的电流和扭矩。下图为扩展后的波形图(PS零水平,手绘):

12个阶段的A、B、C相的状态表也在上图中写出来了,根据这个状态表写每个阶段的程序即可:

void t0_1(void)
{
//	printf("A:0->1 B:-1 C:1\r\n");*A1 = 0;*A2 = 0;*B1 = 0;*B2 = MAX_PWM;*C1 = MAX_PWM;*C2 = 0;int i;for(i=0;i<=MAX_PWM;i++){*A1 = i;delay_us(INTERVAL);}
}void t0_2(void)
{
//	printf("A:1 B:-1 C:1->0\r\n");*A1 = MAX_PWM;*A2 = 0;*B1 = 0;*B2 = MAX_PWM;*C1 = MAX_PWM;*C2 = 0;int i;for(i=0;i<=MAX_PWM;i++){*C1 = MAX_PWM - i;delay_us(INTERVAL);}
}void t1_1(void)
{
//	printf("A:1 B:-1 C:0->-1\r\n");*A1 = MAX_PWM;*A2 = 0;*B1 = 0;*B2 = MAX_PWM;*C1 = 0;*C2 = 0;int i;for(i=0;i<=MAX_PWM;i++){*C2 = i;delay_us(INTERVAL);}
}void t1_2(void)
{
//	printf("A:1 B:-1->0 C:-1\r\n");*A1 = MAX_PWM;*A2 = 0;*B1 = 0;*B2 = MAX_PWM;*C1 = 0;*C2 = MAX_PWM;int i;for(i=0;i<=MAX_PWM;i++){*B2 = MAX_PWM - i;delay_us(INTERVAL);}
}void t2_1(void)
{
//	printf("A:1 B:0->1 C:-1\r\n");*A1 = MAX_PWM;*A2 = 0;*B1 = 0;*B2 = 0;*C1 = 0;*C2 = MAX_PWM;int i;for(i=0;i<=MAX_PWM;i++){*B1 = i;delay_us(INTERVAL);}
}void t2_2(void)
{
//	printf("A:1->0 B:1 C:-1\r\n");*A1 = MAX_PWM;*A2 = 0;*B1 = MAX_PWM;*B2 = 0;*C1 = 0;*C2 = MAX_PWM;int i;for(i=0;i<=MAX_PWM;i++){*A1 = MAX_PWM - i;delay_us(INTERVAL);}
}void t3_1(void)
{
//	printf("A:0->-1 B:1 C:-1\r\n");*A1 = 0;*A2 = 0;*B1 = MAX_PWM;*B2 = 0;*C1 = 0;*C2 = MAX_PWM;int i;for(i=0;i<=MAX_PWM;i++){*A2 = i;delay_us(INTERVAL);}
}void t3_2(void)
{
//	printf("A:-1 B:1 C:-1->0\r\n");*A1 = 0;*A2 = MAX_PWM;*B1 = MAX_PWM;*B2 = 0;*C1 = 0;*C2 = MAX_PWM;int i;for(i=0;i<=MAX_PWM;i++){*C2 = MAX_PWM - i;delay_us(INTERVAL);}
}void t4_1(void)
{
//	printf("A:-1 B:1 C:0->1\r\n");*A1 = 0;*A2 = MAX_PWM;*B1 = MAX_PWM;*B2 = 0;*C1 = 0;*C2 = 0;int i;for(i=0;i<=MAX_PWM;i++){*C1 = i;delay_us(INTERVAL);}
}void t4_2(void)
{
//	printf("A:-1 B:1->0 C:1\r\n");*A1 = 0;*A2 = MAX_PWM;*B1 = MAX_PWM;*B2 = 0;*C1 = MAX_PWM;*C2 = 0;int i;for(i=0;i<=MAX_PWM;i++){*B1 = MAX_PWM - i;delay_us(INTERVAL);}
}void t5_1(void)
{
//	printf("A:-1 B:0->-1 C:1\r\n");*A1 = 0;*A2 = MAX_PWM;*B1 = 0;*B2 = 0;*C1 = MAX_PWM;*C2 = 0;int i;for(i=0;i<=MAX_PWM;i++){*B2 = i;delay_us(INTERVAL);}
}void t5_2(void)
{
//	printf("A:-1->0 B:-1 C:1\r\n");*A1 = 0;*A2 = MAX_PWM;*B1 = 0;*B2 = MAX_PWM;*C1 = MAX_PWM;*C2 = 0;int i;for(i=0;i<=MAX_PWM;i++){*A2 = MAX_PWM - i;delay_us(INTERVAL);}
}

主程序按顺序调用这12个函数:

	//设置所有桥臂为无输出。注意IO_X1和IO_X2信号不可以同时为高,//否则会导致桥臂短路,容易烧坏MOS管,同时在程序中最好先将为0的//信号先设置为0,然后再设置不为0的信号,这样可以避免短路。*A1 = 0;*A2 = 0;*B1 = 0;*B2 = 0;*C1 = 0;*C2 = 0;#define INTERVAL	10		//PWM上升下降间隔时间#define MAX_PWM		255		//处于t5状态,并维持一段时间,保证可靠处于t5状态printf("Ready\r\n");{	//t5:CBprintf("t5:CB\r\n");*A1 = 0;*A2 = 0;*C1 = MAX_PWM;*C2 = 0;*B1 = 0;*B2 = MAX_PWM;}delay_ms(200);while(1){t0_1();//		printf("0_1\r\n");		delay_ms(2000);t0_2();//		printf("0_2\r\n");		delay_ms(2000);t1_1();//		printf("1_1\r\n");		delay_ms(2000);t1_2();//		printf("1_2\r\n");		delay_ms(2000);t2_1();//		printf("2_1\r\n");		delay_ms(2000);t2_2();//		printf("2_2\r\n");		delay_ms(2000);t3_1();//		printf("3_1\r\n");		delay_ms(2000);t3_2();//		printf("3_2\r\n");		delay_ms(2000);t4_1();//		printf("4_1\r\n");		delay_ms(2000);t4_2();//		printf("4_2\r\n");		delay_ms(2000);t5_1();//		printf("5_1\r\n");		delay_ms(2000);t5_2();//		printf("5_2\r\n");		delay_ms(2000);}

这里的INTERVAL是上升延时,和上篇文章中的不是一个含义。调整INTERVAL的大小可以改变转速,程序运行的不错,电机比之前运行丝滑很多,和某巧克力一样。

程序可以停在12个阶段中的任意一个阶段中的任意一个状态,所以可以实现和步进电机一样的步进控制,调大INTERVAL到1000以以上就能看出电机慢慢的转动的现象了,但是不如步进电机,老是会有“突变”的现象,可能就是无刷电机结构问题导致的,因为无刷电机一开始就没这么用的......

再来改一下程序,如下:

void go_stage(int stage)
{if(stage == 0){*A1 = 0;*A2 = 0;*B1 = 0;*B2 = MAX_PWM;*C1 = MAX_PWM;*C2 = 0;}else if(stage == 1){*A1 = MAX_PWM;*A2 = 0;*B1 = 0;*B2 = MAX_PWM;*C1 = MAX_PWM;*C2 = 0;}else if(stage == 2){*A1 = MAX_PWM;*A2 = 0;*B1 = 0;*B2 = MAX_PWM;*C1 = 0;*C2 = 0;}else if(stage == 3){*A1 = MAX_PWM;*A2 = 0;*B1 = 0;*B2 = MAX_PWM;*C1 = 0;*C2 = MAX_PWM;}else if(stage == 4){*A1 = MAX_PWM;*A2 = 0;*B1 = 0;*B2 = 0;*C1 = 0;*C2 = MAX_PWM;}else if(stage == 5){*A1 = MAX_PWM;*A2 = 0;*B1 = MAX_PWM;*B2 = 0;*C1 = 0;*C2 = MAX_PWM;}else if(stage == 6){*A1 = 0;*A2 = 0;*B1 = MAX_PWM;*B2 = 0;*C1 = 0;*C2 = MAX_PWM;}else if(stage == 7){*A1 = 0;*A2 = MAX_PWM;*B1 = MAX_PWM;*B2 = 0;*C1 = 0;*C2 = MAX_PWM;}else if(stage == 8){*A1 = 0;*A2 = MAX_PWM;*B1 = MAX_PWM;*B2 = 0;*C1 = 0;*C2 = 0;}else if(stage == 9){*A1 = 0;*A2 = MAX_PWM;*B1 = MAX_PWM;*B2 = 0;*C1 = MAX_PWM;*C2 = 0;}else if(stage == 10){*A1 = 0;*A2 = MAX_PWM;*B1 = 0;*B2 = 0;*C1 = MAX_PWM;*C2 = 0;}else if(stage == 11){*A1 = 0;*A2 = MAX_PWM;*B1 = 0;*B2 = MAX_PWM;*C1 = MAX_PWM;*C2 = 0;}
}void main(void)
{......//设置所有桥臂为无输出。注意IO_X1和IO_X2信号不可以同时为高,//否则会导致桥臂短路,容易烧坏MOS管,同时在程序中最好先将为0的//信号先设置为0,然后再设置不为0的信号,这样可以避免短路。*A1 = 0;*A2 = 0;*B1 = 0;*B2 = 0;*C1 = 0;*C2 = 0;#define INTERVAL	10		//PWM上升下降间隔时间#define MAX_PWM		255		printf("Ready\r\n");stage = 0;go_stage(stage);delay_ms(200);direction = 0;	//控制转动方向stage = 0;		//当前阶段offset = 0;		//阶段中偏移while(1){if(direction == 0)	//顺时针{offset++;if(offset == MAX_PWM + 1)	//换相{offset= 0;stage++;if(stage == 12)stage = 0;go_stage(stage);}}else{if(offset == 0)	//换相{offset= 255;if(stage == 0)stage = 11;elsestage--;go_stage(stage);}elseoffset--;}switch(stage){case 0	:*A1 = offset;				break;case 1	:*C1 = MAX_PWM - offset;	break;case 2	:*C2 = offset;				break;case 3	:*B2 = MAX_PWM - offset;	break;case 4	:*B1 = offset;				break;case 5	:*A1 = MAX_PWM - offset;	break;case 6	:*A2 = offset;				break;case 7	:*C2 = MAX_PWM - offset;	break;case 8	:*C1 = offset;				break;case 9	:*B1 = MAX_PWM - offset;	break;case 10	:*B2 = offset;				break;case 11	:*A2 = MAX_PWM - offset;	break;}delay_us(INTERVAL);}
}

通过调整direction和INTERVAL的值可以修改转动方向和转动速度,改变MAX_PWM可以调整电流大小。抓取一下波形看看6个通道的PWM信号的12个阶段,其中白色的部分是PWM信号:

以第一个阶段也就是程序里面的 t0_1 阶段为例,6个通道首先处于 t5:CB 的初始化状态,然后A1通道从0%到100%渐变,A2通道为0%,B1、B2、C1、C2分别为0%、100%、100%、0%,和我们设计的波形是一样的。

程序里面可以把PWM的细分数降低,我改成(10 - 1)(这里 -1 是为了当PWM取值10时占空比为100%,与具体单片机内部定时器的PWM实现机制相关),然后将分频数增加到500,因为没必要细分那么多级,MAX_PWM的取值范围为 0 到 10 ,这样INTERVAL的值也可以放大到 ms 级别,可以放在一个定时时间为INTERVAL的定时器中断中来处理电机的驱动,如果INTERVAL很小的话,会导致一直在定时器中断中出不来,示例程序:

    TIM1_PWM_Init(10 - 1,500);    //48M / 500 / 10 = 9.6KHzTIM3_PWM_Init(10 - 1,500);TIM14_PWM_Init(10 - 1,500);#define INTERVAL    1000		//PWM上升下降间隔时间#define MAX_PWM    10		

然后我将电机驱动电压改成12V的电源,调整MAX_PWM的值分别测量电流大小,我测试使用电机在满PWM的时候能达到3A的电流,在50%的占空比的时候电流为0.4A左右。不通电机和供电电压需要匹配不同的INTERVAL和MAX_PWM的值才能转的好。

这篇关于【单片机】DIY无刷电机驱动器 2的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/921399

相关文章

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机

如何编写Linux PCIe设备驱动器 之二

如何编写Linux PCIe设备驱动器 之二 功能(capability)集功能(capability)APIs通过pci_bus_read_config完成功能存取功能APIs参数pos常量值PCI功能结构 PCI功能IDMSI功能电源功率管理功能 功能(capability)集 功能(capability)APIs int pcie_capability_read_wo

三相直流无刷电机(BLDC)控制算法实现:BLDC有感启动算法思路分析

一枚从事路径规划算法、运动控制算法、BLDC/FOC电机控制算法、工控、物联网工程师,爱吃土豆。如有需要技术交流或者需要方案帮助、需求:以下为联系方式—V 方案1:通过霍尔传感器IO中断触发换相 1.1 整体执行思路 霍尔传感器U、V、W三相通过IO+EXIT中断的方式进行霍尔传感器数据的读取。将IO口配置为上升沿+下降沿中断触发的方式。当霍尔传感器信号发生发生信号的变化就会触发中断在中断

单片机毕业设计基于单片机的智能门禁系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍程序代码部分参考 设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订

为备份驱动器制定备份计划:维护数据的3大方法

时间:2014-02-26 14:49 来源:网管之家 字体:[大 中 小]   您可能已经对您的电脑进行了备份,但其实这样还是远远不够的,其并非如您所认为的那样安全。您企业备份驱动器上的文件可能与您的主系统上的文件一样,容易受到灾难的影响。根据最近流行的恶意软件CryptoLocker的感染途径显示,连接到PC的外置驱动器——辅助硬盘驱动器,例如,用于备份的外部USB硬盘驱动器,可以像

基于stm32的河流检测系统-单片机毕业设计

文章目录 前言资料获取设计介绍功能介绍具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机设计精品

VB和51单片机串口通信讲解(只针对VB部分)

标记:该篇文章全部搬自如下网址:http://www.crystalradio.cn/thread-321839-1-1.html,谢谢啦            里面关于中文接收的部分,大家可以好好学习下,题主也在研究中................... Commport;设置或返回串口号。 SettingS:以字符串的形式设置或返回串口通信参数。 Portopen:设置或返回串口

MCU5.51单片机的最小系统

1.最小系统的组成部分 晶振电路(时钟),复位电路,电源电路(控制电压,保持稳定),下载电路(外加的,用于烧录程序) 烧录: 通过下载电路,把程序下载到单片机中用于运行 2.晶振电路 电路图: 是晶振 是电容 为了防止晶振起振时产生影响,因此加电容 3.复位电路 电路图: 按按钮RSTK1(保持一段时间的高电平)即可实现STC芯片的复位 4.电源电路 电路图:

单片机XTAL引脚引出的晶振分析

51单片机的18,19脚XTAL1,XTAL2用来提供外部振荡源给片内的时钟电路。 XTAL1和XTAL2引脚,该单片机可以使用外部时钟也可以使用内部时钟。 当使用内部时钟时,此二引线端用于外接石英晶体和微调电容; 当使用外部时钟时,用于接外部时钟信号,NMOS接XTAL2,CMOS接XTAL1。 原理: XTAL1和XTAL2分别是一个反相器的输入和输出。NMOS的反相器是

如何编写Linux PCI设备驱动器 之一

如何编写Linux PCI设备驱动器 之一 PCI寻址PCI驱动器使用的APIpci_register_driver()pci_driver结构pci_device_id结构 如何查找PCI设备存取PCI配置空间读配置空间APIs写配置空间APIswhere的常量值共用部分类型0类型1 PCI总线通过使用比ISA更高的时钟速率来实现更好的性能;它是时钟运行在 25 或 33 M