基于STM32(HAL库)的直流无刷电机驱动(含AS5048A磁编码器)失败踩坑历程,为后来人做一些Tips!

您所在的位置:网站首页 无刷电机如何实现换向的 基于STM32(HAL库)的直流无刷电机驱动(含AS5048A磁编码器)失败踩坑历程,为后来人做一些Tips!

基于STM32(HAL库)的直流无刷电机驱动(含AS5048A磁编码器)失败踩坑历程,为后来人做一些Tips!

2024-07-07 16:38:51| 来源: 网络整理| 查看: 265

共享,接受,客观,进步!         先说明一下任务,本次任务需要设计一个类似云台的摄像头支架。由于要求尺寸小,所以市面上电机选型较为局限,所以选择了直流无刷电机(三相驱动)含磁编码传感器。之前并未有STM32与“三相直流电机”基础,路上遇到很多坑,希望为大家提供一个参考!虽然实验没有顺利完成,但程序与硬件做了尝试,现怀疑是换向程序写的有问题与硬件电路无法实现功能的问题。如果有条件可以使用示波器等实验设备进行进一步调整。其方案设计图如下:     

             要驱动电机就要搞清楚原理,原理倒是很多博客有写,都是通过6桥电路实现的电子换向,但大多数都是上桥臂或下桥臂的二者其一是PWM,余下一臂则是常开,但从实际工程来说,我认为通过不断换向,常开桥臂也算是PWM波形了。(因为不会一直是一相导通)具体参考链接如下:          1 PWM互补输出 > https://blog.csdn.net/weixin_45910070/article/details/125228128

 2直流无刷电机应用思路

> https://blog.csdn.net/qq_15181569/article/details/119865081

 3 直流无刷电机原理(写的很好)

> https://zhuanlan.zhihu.com/p/653610405

硬件原理图参考:

        其中我们设A、B、C接口为直流无刷电机的U、V、W三相,从理论上来说导通给V1、V4高电平即电机的U、V两相导通,依次类推就能实现换向的功能。值得注意的是,是需要自举电路的!之前因为太想当然了,忽略了MOS管的GS导通电压。也就是图中V1点与A点两点电压需要大于Ugs(导通电压),所以需要依靠额外的驱动芯片IRS2101S,来为上桥臂提供逻辑电压。         而IRS2101s需要两个输入端都是PWM端,如果其中一端长时间通高电平芯片会特别烫(已经烫了两个水泡!),所以现在也不知道该芯片是否被烧毁,属实有些不严谨。

        有了电路图与原理,就开始写代码吧!

        软件需求:

        1、需要3路占空比可调的互补PWM(即6路PWM),使用STM32的TIM1定时器来实现,通过完全映射管脚至PE8、PE9、PE10、PE11、PE12、PE13。

        2、需要读取AS5048a的角度数据,本次只使用的PWM模式读取的,该款芯片通过检测磁场相对角度变化,输出的PWM占空比随角度变化。

        3、需要PID算法对角度信息进行调节(占空比调节),使电机运动到指定角度,并且保持。

        4、需要按键设定目标角度。

1、PWM程序

void TIM1_PWM_Init(uint16_t arr,uint16_t psc) { GPIO_InitTypeDef gpio_init_struct = {0}; TIMX_CLK_ENABLE(); HIN1_PIN_CLK_ENABLE(); HIN2_PIN_CLK_ENABLE(); HIN3_PIN_CLK_ENABLE(); LIN1_PIN_CLK_ENABLE(); LIN2_PIN_CLK_ENABLE(); LIN3_PIN_CLK_ENABLE(); gpio_init_struct.Pin = GPIO_PIN_9|GPIO_PIN_11|GPIO_PIN_13; gpio_init_struct.Mode = GPIO_MODE_AF_PP; gpio_init_struct.Pull = GPIO_PULLUP; gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(PIN_PORT,&gpio_init_struct); gpio_init_struct.Pin = GPIO_PIN_8|GPIO_PIN_10|GPIO_PIN_12; gpio_init_struct.Mode = GPIO_MODE_AF_PP; gpio_init_struct.Pull = GPIO_PULLUP; gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(PIN_PORT,&gpio_init_struct); //将PE映射至TIM1 TIMX_CHN_GPIO_REMAP(); TIM1_PWM_handle.Instance = TIM1; TIM1_PWM_handle.Init.Prescaler = psc; TIM1_PWM_handle.Init.CounterMode = TIM_COUNTERMODE_UP; TIM1_PWM_handle.Init.Period = arr; TIM1_PWM_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_PWM_Init(&TIM1_PWM_handle); tim_oc_cplm_pwm.OCMode = TIM_OCMODE_PWM1; tim_oc_cplm_pwm.OCPolarity = TIM_OCPOLARITY_HIGH; tim_oc_cplm_pwm.OCNPolarity = TIM_OCNPOLARITY_HIGH; tim_oc_cplm_pwm.OCIdleState = TIM_OCIDLESTATE_SET; tim_oc_cplm_pwm.OCNIdleState = TIM_OCNIDLESTATE_SET; // 使能TIM1 3通道 HAL_TIM_PWM_ConfigChannel(&TIM1_PWM_handle,&tim_oc_cplm_pwm,TIM_CHANNEL_1); HAL_TIM_PWM_ConfigChannel(&TIM1_PWM_handle,&tim_oc_cplm_pwm,TIM_CHANNEL_2); HAL_TIM_PWM_ConfigChannel(&TIM1_PWM_handle,&tim_oc_cplm_pwm,TIM_CHANNEL_3); g1_sbreak_dead_time_config.OffStateRunMode = TIM_OSSR_ENABLE; /* 运行模式的关闭输出状态 */ g1_sbreak_dead_time_config.OffStateIDLEMode = TIM_OSSI_ENABLE; /* 空闲模式的关闭输出状态 */ g1_sbreak_dead_time_config.LockLevel = TIM_LOCKLEVEL_OFF; /* 不用寄存器锁功能 */ g1_sbreak_dead_time_config.DeadTime = 0; g1_sbreak_dead_time_config.BreakState = TIM_BREAK_DISABLE; /* 使能刹车输入 */ g1_sbreak_dead_time_config.BreakPolarity = TIM_BREAKPOLARITY_HIGH; /* 刹车输入有效信号极性为高 */ g1_sbreak_dead_time_config.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE; /* 使能AOE位,允许刹车结束后自动恢复输出 */ HAL_TIMEx_ConfigBreakDeadTime(&TIM1_PWM_handle, &g1_sbreak_dead_time_config); //HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_1); // HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_2); // HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_3); __HAL_TIM_MOE_ENABLE(&TIM1_PWM_handle); }

2、六步换向(个人感觉使用HAL库很麻烦,无法对TIM1_CHx单独进行操作) ,这个函数实现的功能就是:以其中case1为例,开启对应硬件电路中的V1&&V2&&V4,由于V1、V2这对是互补PWM(并设置了死区),并不会导致电源与GND短接。这样相当于导通了电机中的(+U、-V)相。

int pwm_drive(uint16_t angle,uint16_t pwm_width) { switch (angle) { case 1:{ __HAL_TIM_SetCompare(&TIM1_PWM_handle,TIM_CHANNEL_1,pwm_width); HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(&TIM1_PWM_handle, TIM_CHANNEL_1); __HAL_TIM_SetCompare(&TIM1_PWM_handle,TIM_CHANNEL_2,set_ARR); HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_2); HAL_TIMEx_PWMN_Start(&TIM1_PWM_handle, TIM_CHANNEL_2); HAL_TIM_PWM_Stop(&TIM1_PWM_handle,TIM_CHANNEL_3); HAL_TIMEx_PWMN_Stop(&TIM1_PWM_handle, TIM_CHANNEL_3); return 1; };break; case 2:{ __HAL_TIM_SetCompare(&TIM1_PWM_handle,TIM_CHANNEL_1,pwm_width); HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(&TIM1_PWM_handle, TIM_CHANNEL_1); __HAL_TIM_SetCompare(&TIM1_PWM_handle,TIM_CHANNEL_3,set_ARR); HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_3); HAL_TIMEx_PWMN_Start(&TIM1_PWM_handle, TIM_CHANNEL_3); HAL_TIM_PWM_Stop(&TIM1_PWM_handle,TIM_CHANNEL_2); HAL_TIMEx_PWMN_Stop(&TIM1_PWM_handle, TIM_CHANNEL_2); return 2; };break; case 3:{ __HAL_TIM_SetCompare(&TIM1_PWM_handle,TIM_CHANNEL_2,pwm_width); HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_2); HAL_TIMEx_PWMN_Start(&TIM1_PWM_handle, TIM_CHANNEL_2); __HAL_TIM_SetCompare(&TIM1_PWM_handle,TIM_CHANNEL_3,set_ARR); HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_3); HAL_TIMEx_PWMN_Start(&TIM1_PWM_handle, TIM_CHANNEL_3); HAL_TIM_PWM_Stop(&TIM1_PWM_handle,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Stop(&TIM1_PWM_handle, TIM_CHANNEL_1); return 3; };break; case 4:{ __HAL_TIM_SetCompare(&TIM1_PWM_handle,TIM_CHANNEL_2,pwm_width); HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_2); HAL_TIMEx_PWMN_Start(&TIM1_PWM_handle, TIM_CHANNEL_2); __HAL_TIM_SetCompare(&TIM1_PWM_handle,TIM_CHANNEL_1,set_ARR); HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(&TIM1_PWM_handle, TIM_CHANNEL_1); HAL_TIM_PWM_Stop(&TIM1_PWM_handle,TIM_CHANNEL_3); HAL_TIMEx_PWMN_Stop(&TIM1_PWM_handle, TIM_CHANNEL_3); return 4; };break; case 5:{ __HAL_TIM_SetCompare(&TIM1_PWM_handle,TIM_CHANNEL_3,pwm_width); HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_3); HAL_TIMEx_PWMN_Start(&TIM1_PWM_handle, TIM_CHANNEL_3); __HAL_TIM_SetCompare(&TIM1_PWM_handle,TIM_CHANNEL_1,set_ARR); HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(&TIM1_PWM_handle, TIM_CHANNEL_1); HAL_TIM_PWM_Stop(&TIM1_PWM_handle,TIM_CHANNEL_2); HAL_TIMEx_PWMN_Stop(&TIM1_PWM_handle, TIM_CHANNEL_2); return 5; };break; case 6:{ __HAL_TIM_SetCompare(&TIM1_PWM_handle,TIM_CHANNEL_3,pwm_width); HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_3); HAL_TIMEx_PWMN_Start(&TIM1_PWM_handle, TIM_CHANNEL_3); __HAL_TIM_SetCompare(&TIM1_PWM_handle,TIM_CHANNEL_2,set_ARR); HAL_TIM_PWM_Start(&TIM1_PWM_handle,TIM_CHANNEL_2); HAL_TIMEx_PWMN_Start(&TIM1_PWM_handle, TIM_CHANNEL_2); HAL_TIM_PWM_Stop(&TIM1_PWM_handle,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Stop(&TIM1_PWM_handle, TIM_CHANNEL_1); return 6; };break; //default : return 0; } }

使用keil自带的debug逻辑分析仪,可以看到波形是对的(由于使用得还不是很熟练,对于STM32寄存器的值还不知如何查看,希望有大侠不吝指导!)。

如果大家不会使用keil的逻辑分析仪,请参考这个博客(非常感谢):

keil仿真和使用示波器调波形_keil仿真波形-CSDN博客

 3、PWM输入捕获,本部分直接使用正点原子中的PWM输入捕获实验所做。通过使用TIM8来实现的。这里就不贴代码了,需要注意的是在例程当中TIM8输入捕获的管脚模式设置为推挽复用功能,导致外部输入无法检测(低级失误),将管脚模式设置为输入下拉模式,即可完成对AS5048a输出PWM的检测!

4、PID算法,参考了该博客:https://blog.csdn.net/qq_15181569/article/details/119865081,但由于我硬件并未实现,也没有进行调参。。大家可以根据自己实际工程,进行修改。

typedef struct PID { int Traget; // 目标值 int Uk; // 输入 int Udk; //输入增量 int Uk_1; // 上一时刻输入 double P; double I; double D; int b; int ek_0; int ek_1; int ek_2; }PID; static PID Pos_PID; static PID *Angle_Point = &Pos_PID; // 角度PID 初始化 void Angle_PIDInit(void) { Angle_Point->Traget = start_angle; Angle_Point->Uk = 0; Angle_Point->Udk = 0; Angle_Point->Uk_1 = PWM_min; Angle_Point->ek_0 = 0; Angle_Point->ek_1 = 0; Angle_Point->ek_2 = 0; Angle_Point->P = 4; //比例系数(需要根据工程实例调) Angle_Point->I = 0.084; Angle_Point->D = 1.8; Angle_Point->b = 1; } // 使用PID计算出需要的PWM占空比,即比较值 int Angle_PIDAdjust(int target_angle,int now_point) { Angle_Point->ek_0 = target_angle - now_point; if(((Angle_Point->Uk_1>=PWM_max)&&(Angle_Point->ek_0>=0))||((Angle_Point->Uk_1ek_0b = 0; } else { Angle_Point->b = 1; } Angle_Point->Udk = Angle_Point->P * (Angle_Point->ek_0 - Angle_Point->ek_1) + Angle_Point->b * Angle_Point->I * Angle_Point->ek_0 + Angle_Point->D * (Angle_Point->ek_0 - 2 * Angle_Point->ek_1 + Angle_Point->ek_2); Angle_Point->Uk = Angle_Point->Uk_1 + Angle_Point->Udk; Angle_Point->ek_2 = Angle_Point->ek_1; Angle_Point->ek_1 = Angle_Point->ek_0; Angle_Point->Uk_1 = Angle_Point->Uk; if (Angle_Point->Uk >= PWM_max) { return PWM_max; } else if (Angle_Point->Uk Uk); }

5、按键输入,这个在这里就不赘述了,比较简单。

6、最最最重要的就是换向程序,即要知道磁极的位置,通过位置对绕组进行电流上的换向,本次实验失败非常有可能是因为这个程序没有写对!先说一下自己的理解,之前看到一篇论文中讲:先接入电机的任意两相,待电机稳定后,磁极是处于相邻两相之间的,再加上360度电角度,反映至机械角度就是60度,因为极对数为6。所以我就按照60度机械角度为一个电角度周期,即10度一换相。网上大多是BLDC的电机换向程序,没有找到使用编码器的换向程序,所以做的浑浑噩噩,希望有大侠指点一二!

int huanxiang(int angle) { if (((340


【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭