STM32
直播中

欲望都市

9年用户 902经验值
擅长:可编程逻辑
私信 关注
[问答]

***

受到警告
提示: 作者被禁止或删除 内容自动屏蔽

回帖(1)

王波

2021-11-19 11:46:45
  本文记录博主学习BLDC控制软件的过程。
  1 概述
  本文是博主第一次做BLDC控制实验,内容只是写代码让BLDC转起来,而暂时不考虑其他复杂的需求。
  控制的方法是比较简单的六步换相法。利用电机内部的霍尔传感器,通过参考电机厂家给出的二二导通换相表,生成方波来控制电机。第一次实验为了简化需求,只考虑开环、定速、正转的情况:
  开环:不考虑速度反馈,直接给PWM让电机转起来;
  定速:不考虑速度调节,换相的时候给的PWM都是相同的值;
  正转:只参考换相表中正转的部分,不考虑反转;
  所以,实验目标为,通过按下开发板上的Key1按键使BLDC低速运转,按下Key2按键使BLDC停止运转。
  本实验需要BLDC六步换相的理论作为知识储备。
  2 软件架构及工具链
  2.1 软件架构
  博主根据自己的工作习惯和经验,制订出实验的软件架构如下:
  
  其中,各个板块的含义如下:
  ASW:应用层软件,包含电机控制策略和算法;
  BSW:底层软件,包含驱动Stm32的寄存器配置;
  Interface:接口层软件,为底层和应用层之间的接口函数;
  UI:调试开发板所用的代码;
  2.2 工具链
  对于架构中不同的板块,博主也根据经验选择合适的工具链进行开发。
  Matlab/Simulink:基于模型开发的利器,非常适用于应用层软件开发。首先在Simulink中创建控制策略的模型,再用Embedded Coder组件生成C代码;
  CubeMX:ST公司开发的用于配置STM32的工具,通过配置时钟和外设引脚,可以生成标准的HAL库,可用于BSW代码的生成;
  MDK:STM32的编译环境,用于编译、调试软件并下载到控制器中,Interface的手写代码也在其中完成;
  串口调试助手:用于监测程序运行过程中的一些变量值;
  3 底层代码生成
  本章节会结合原理图,研究配置CubeMX生成底层代码。
  3.1 时钟配置
  博主用的开发板是STM32F405RGT6,在CubeMX中将时钟源设置为外部晶振。
  
  时钟数配置如下图。
  
  由于后续的低频任务会运行在系统定时器中断里,所以这里需要留意一下系统定时器的频率为168MHz,也就是1秒钟计数168000000次。
  3.2 按键输入配置
  在本实验中,开发板上的按键是用于告诉单片机启动或停止电机的请求。开发板上焊接了5个按键模块,如下图。本实验只需要用SW1和SW2两个。
  
  以SW1为例,它的左端接地,右端连接到单片机的PC13引脚,如下图。
  
  所以在CubeMX中配置PC13引脚的模式为输入模式,并配置为上拉。
  
  这样的配置表示,PC13引脚接收到低电平时,按键SW1被按下,接收到高电平时,按键SW1没有被按下。SW2的按键引脚配置类似。
  3.3 串口通信配置
  串口通信可以将单片机运行过程中的全局变量发送到PC上,然后通过串口调试助手读取信息,是一种验证问题的常规方式。
  从原理图可以看出,PB10和PB11引脚是串口3发送和接收的引脚。
  
  首先将串口模式设置为Asynchronous,也就是异步通信。
  
  然后是一些参数设置,包括波特率、字长度等。这里的设置将和PC上的串口调试助手相对应。
  
  最后是NVIC中断优先级设置,这里暂时设置为如下图。
  
  3.4 霍尔信号外部中断配置
  六步换相法控制BLDC需要根据霍尔传感器信号输入来判断导通哪些MOS管,因此需要配置3个引脚来分别接收A相,B相,C相三个霍尔信号。
  
  从原理图中可以看出,PB6,PB7,PB8这三个引脚分别对应着A相,B相,C相,所以接下来需要在CubeMX中配置这三个引脚。以PB6为例,在CubeMX中如下。
  
  
  首先,GPIO_EXTI表示用于GPIO的外部中断。GPIO mode配置成了上升或下降沿触发的外部中断,也就是说,当电机转动导致了霍尔信号变化的时候,会触发外部中断。这一点很重要,因为在该中断回调函数里,会执行换相逻辑。
  3.5 MOS管导通开关配置
  STM32通过控制引脚输出PWM或GPIO电平,来导通MOS管,从而实现了BLDC的换相控制。由于采用了H PWM-L ON的控制方式,上桥由PWM驱动,下桥由高低电平驱动。
  以A相为例,原理图如下。左边的TIM1_CH是PWM,TIM1_CHN是高低电平。
  
  然后顺藤摸瓜找到对应的引脚,TIM1_CH对应PA8,TIM1_CHN对应PB13。
  
  接着就可以在CubeMX分别配置PWM输出和GPIO输出。
  
  这里博主将上桥PWM配置为Up向上计数,重装载值为12000。也就是说后面进行输出比较的时候,范围是0~12000。
  
  下桥的GPIO配置为推挽输出,初始为低电平。
  以上配置完成后,就可以生成Keil工程了。
  4 接口及配置代码
  本章节会在Keil中手写一些代码,作为两方面用处。一方面是为应用层软件配置好输入输出接口函数,另一方面是调用Hal库函数对底层进一步配置。
  4.1 系统定时器重装载值
  在main文件的初始化代码段加上如下函数:
  HAL_SYSTICK_Config(168000U);
  该函数用于配置系统定时器重装载值为168000。结合前文将系统时钟配置为168MHz,重装载值配置为168000就意味着通过系统定时器产生1ms中断。在中断函数SysTick_Handler中将执行低频任务。
  4.2 按键输入接口函数
  在main文件中配置如下按键输入接口函数,调用该函数可以返回按键状态(1或0)。
  // Get Key State
  uint8_t Get_Key1State(void)
  {
  return((uint8_t)(!HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)));
  }
  uint8_t Get_Key2State(void)
  {
  return((uint8_t)(!HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_0)));
  }
  博主习惯于将输入的接口函数以Get_开头,其实相当于封装了一层统一的命名,并简化传参。这里的Get函数中调用了Hal库的读取GPIO pin脚的函数,并对返回值取反,使得返回1代表按键按下,0代表按键没有按下。
  4.3 重定向printf函数
  在main文件中重新定义fputc 函数:
  int fputc(int ch, FILE *f)
  {
  HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xFFFF);
  return (ch);
  }
  通过这种方式,就可以用printf函数将全局变量打印到PC上。本文的实验比较简单,没有做UI上位机,就用串口调试助手查看电机运行的实时数据了。
  4.4 霍尔信号状态函数
  在main文件中配置霍尔信号状态接收函数,调用该函数可以返回霍尔传感器状态(1或0)。
  // Get Hall State
  uint8_t Get_HallAState(void)
  {
  return HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6);
  }
  uint8_t Get_HallBState(void)
  {
  return HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7);
  }
  uint8_t Get_HallCState(void)
  {
  return HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8);
  }
  应用层调用霍尔信号状态函数可以获取当前的A,B,C三相的霍尔传感器状态,然后查阅换相表得到导通的MOS管。
  4.5 MOS管导通控制函数
  在main文件中配置MOS管导通控制函数,调用该函数可以设置MOS管的开关或者PWM。
  // Mos
  void Set_T1PWM(uint32_t PWMValue)
  {
  __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,PWMValue);
  }
  void Set_T3PWM(uint32_t PWMValue)
  {
  __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,PWMValue);
  }
  void Set_T5PWM(uint32_t PWMValue)
  {
  __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_3,PWMValue);
  }
  void Set_T2Status(uint8_t State)
  {
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13,(GPIO_PinState)State);
  }
  void Set_T4Status(uint8_t State)
  {
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14,(GPIO_PinState)State);
  }
  void Set_T6Status(uint8_t State)
  {
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15,(GPIO_PinState)State);
  }
  其中,T1,T3,T5是上桥,在CubeMX配置为定时器PWM,因此传入的是输出比较值;T2,T4,T6是下桥,配置为GPIO输出,因此传入0或1的状态,来控制导通与断开。这里传入的PWMValue是0~12000,因为CubeMX中配置重装载值为12000。
  4.6 电机启停函数
  在main文件中配置电机启停函数,调用该函数可以启动或停止定时器通道,从而达到启停电机的效果。
  // Set_StartStopMotor
  void Set_StartStopMotor(uint8_t State)
  {
  if(State == 0) //Stop Motor
  {
  HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_1);
  HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_2);
  HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_3);
  }
  else
  {
  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3);
  }
  }
  Set函数会根据传入的State参数来判断调用Stop或是Start的Hal库函数。
  5 应用层模型及代码生成
  本实验的电机控制应用层软件采用Simulink建模并生成代码。
  5.1 基于模型设计(MBD)方案
  Matlab/Simulink软件的灵活性很强,可以定制化生成各式各样的代码。博主按照自己的建模习惯,制定了本小节的MBD方案,但不是Matlab/Simulink的唯一用法。
  首先,将所有控制策略做在一个模型文件和数据字典中,命名为MotorControl,如下图所示。
  
  数据字典中定义了枚举量、Signal对象等,并根据需要设定Storage Class和头文件。
  
  模型的顶层调用了两个function-call子系统,分别对应系统定时器中断中运行的低频任务,和霍尔传感器外部中断中运行的换相任务。在代码生成的时候会生成SystickTask和HallExitTask两个函数。
  
  下面博主会叙述自己设计的BLDC控制策略,如有不当之处,希望能够交流探讨。
  5.2 低频任务子系统
  本实验中的低频任务只做两件事,电机状态机建模、启动停止控制。输入为按键状态、霍尔传感器状态,输出为电机状态。
  1)首先是按键输入监测,调用了Get_Key1State()接口函数,并通过延时两个周期判断产生了上升沿。
  
  这部分做了两个延时,也就是,第一个周期Key1State为0,然后连续两个周期为1的情况时,视为一次启动电机的请求(StartMotorReq置为1)。
  2)电机状态机,Stateflow中共有4个状态:Init,Start,Run,Stop。
  
  电机状态机的输入为启动或停止电机的请求。默认进入Init初始化状态,如果产生了启动电机请求(StartMotorReq)则跳转到Start状态。如果在Start状态停留了10个周期(StartCount == 10),则进入Run状态。当产生了停止电机请求(StopMotorReq),无论当前为Start状态还是Run状态都会跳入Stop状态。
  进入Start或是Stop状态的时候,会产生一个事件(Event)用于触发外面的子系统。
  每个状态进入时,都会输出MotorState,作为状态机外面的逻辑判断的依据。
  3)电机启动的时候还未进入霍尔传感器引起的中断,在该状态中也需要进行启动时的换相函数。首先通过Get_HallXState函数获取三个霍尔传感器的状态,再通过移位进行组装合成。
  
  A相左移2位,B相左移1位,C相不移位。例如A,B,C三相分别是1,0,1时,
  HallState = (A 《《 2) + (B 《《 1) + C = 4 + 0 + 1 = 5 后面的模型会通过HallState 输入到查表模块(1D Lookup Table)中来判断哪些MOS管需要导通。然后通过调用Set函数导通Mos管。
  
  PWMValue的值在数据字典中定义为2000。这是因为本实验做开环定速旋转,设的过高会引起启动时的抖动。
  5.3 霍尔中断子系统
  霍尔中断子系统中的模型中没有电机状态机的部分,其余的和5.2 低频任务子系统相似,也是根据霍尔传感器的状态判断导通MOS管。但是有一点区别在于该子系统需要输出HallExtiFlag,也就是霍尔激活标志位。
  
  5.4 代码生成及调用
  将模型MotorControl.slx配置好Embedded Coder后,Ctrl + B生成代码。
  
  顶层的两个子系统分别生成了HallExtiTask.c和SystickTask.c两个c文件,并且其中包含了同名的函数。这两个函数就是上文两个子系统对应的代码。
  
  MotorControl.c中生成了一个初始化函数MotorControl_initialize(),用于将模型中定义的全局变量初始化为0。
  将所有的C文件和头文件拷贝到Keil工程中,然后在三个地方分别调用。
  1)在main函数中调用初始化函数MotorControl_initialize();
  
  2)在系统定时器中断函数中调用SystickTask();
  
  3)在霍尔传感器中断回调函数中调用HallExtiTask();
  
  另外,调用函数需要加上对应的头文件,否则编译无法通过。
  最后在Keil中编译软件,并通过ST-Link下载到单片机中。尝试按下按键1和按键2,验证是否可以启动和停止。
  6 总结
  博主第一次做电机控制实验,感觉还是很有意思的。后面会按照本文的方法论去继续研究BLDC的控制。
举报

更多回帖

相关问答

BLDC 换相 控制软件
    发帖
    ×
    20
    完善资料,
    赚取积分