本文记录博主学习BLDC控制软件的过程。
文章目录
1 概述
本文是博主进一步学习BLDC控制的实验。在上一篇博客《BLDC控制实验:方波、霍尔、开环、定速、正转》的基础上,加入了调速的功能,包括以下两方面:
- 通过旋转电位器,调整BLDC转速大小;
- 通过霍尔传感器和定时器计数算出BLDC当前转速大小;
在上一篇博客中已经实现的换相和按键输入等功能,本文不再赘述。方法和工具链和上一篇类似,也是底层、接口、应用层的顺序进行实现。
2 底层代码生成
在《BLDC控制实验:方波、霍尔、开环、定速、正转》中关于时钟树、GPIO、PWM等配置的基础上,进一步配置CubeMX生成底层代码。
2.1 电位器输入配置
开发板上焊接了可调电位器和10k的电阻,组成了一个可调分压电路。该电路可以作为调速用的速度请求输入信号。
右边的电阻也是10k,SPEEDREF引脚接在了两个电阻之间。容易算出SPEEDREF引脚的电压理论上变化范围是3.3/2 V~ 3.3V。
SPEEDREF连接在STM32芯片上的PB0引脚上,因此在CubeMX中配置PB0引脚的ADC采样。
如图所示,配置ADC1为独立单通道采集。其中分辨率(Resolution)设为12位,并设为连续转换模式。
这样的话,就可以生成一些函数来获取PB0引脚的AD转换值。
2.2 TIM3定时器配置
霍尔传感器可以反馈位置状态,如果要将其计算为速度,则还需要一个时间变量。TIM3定时器配置就是为了利用芯片的资源,获取时间信息。
由于TIM1定时器已经配置为了PWM,所以采用TIM3中断来获取时间,TIM3在CubeMX中配置如下。
预分频设为41,向上计数,重装载值为99,并且开启了中断。因此TIM3中断周期计算方法如下。
TIM3所挂的时钟是APB1,时钟树中配置为42MHz。预分频为41表示TIM3的频率为42 / (41+1) = 1MHz,即1us计数一次。重装载值为99表示计数0~99后进入一次中断,即100个1us会中断一次,即0.1ms中断一次,然后在中断服务函数中可以写上相应的程序。
3 接口及配置代码
3.1 ADC转换接口函数
在main文件中配置如下ADC转换接口函数,调用该函数可以返回PB0的ADC转换值。
//ADC
uint16_t Get_ADC1Value(void)
{
uint16_t adc_value;
HAL_ADC_Start(&hadc1);//启动ADC单次转换
HAL_ADC_PollForConversion(&hadc1, 50);//等待ADC转换完成
adc_value = HAL_ADC_GetValue(&hadc1);//读取ADC转换数据
return adc_value;
}
Get_ADC1Value函数中调用了HAL库的函数,完成一次ADC转换。由于在CubeMX中将分辨率配置为12位,因此ADC返回的数值应该是0 - 4096,对应了0 - 3.3v。由于电路图中还有个电阻分去了一半的电压,因此ADC返回数值的范围进一步缩减为2048 - 4096,且实际过程中会有一定偏差。
3.2 TIM3计数接口函数
在main文件中配置如下TIM3计数接口函数以及TIM3中断回调函数,调用接口函数可以返回TIM3计数100us的次数。
//TIM3
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == htim3.Instance)
{
Count100us++;
}
}
uint32_t Get_TIM3Count(void)
{
return Count100us;
}
其中,HAL_TIM_PeriodElapsedCallback是TIM定时器中断服务函数中调用的回调函数。在该函数中判断如果是tim3中断,就把全局变量Count100us加1。因此Count100us就代表了在使能TIM中断后计数了多少了100us。
下面的Get_TIM3Count接口函数就是用来返回Count100us这个全局变量的值,可以由模型调用该函数。
另外要记得在主函数中使能中断:
HAL_TIM_Base_Start_IT(&htim3);
4 应用层模型及代码生成
4.1 PWM请求计算模块
本文还是做开环控制,相对于之前的实验只是将固定的PWM=2000改为由电位器输入改变PWM的数值。因此需要通过电位器的ADC转换值,算出对应的PWM。
1)首先需要对采集的ADC数值进行均值计算,减少跳变的影响,模型如下。
调用接口函数Get_ADC1Value得到当前的ADC值,用Unit Delay模块与前两个周期的值相加并除以3,算出3个周期内的ADC1均值。
2)由ADC1均值算出请求的PWM数值。
上文提到,ADC1返回的数值是0 - 4096,被分压电阻分走了一半,范围就变成了 2048 - 4096。所以模型中就先减去了2048,再将0 - 2048映射到0 - 12000的PWM。最后用Saturation模块把范围限值成2000 - 10000。
3)换相模块中输入的PWM请求。
修改两个子系统中换相相关的模块,把原来固定的PWM常数改成前两步基于电位器AD转换数值算出来的PWM请求值。
4.2 转速计算模块
转速计算模块在Hall中断子系统中进行,原理就是当霍尔中断了一定的次数后,算一下每次中断所用的时间,从而换算成电机的rmp单位(转每分钟)。
如下图状态机所示,输入为电机当前状态和TIM3中断计数值,输出为转速。
在状态机内部,分别设定3个状态:Reset,Calc和Wait。
1)当MotorState为Run以外的状态时,会从Wait或Calc跳转会Reset状态,然后霍尔中断计数(HallCount)和速度(MotorSpeedHall)都会置为0;
2)当MotorState为Run状态时,从Reset跳转到Wait,同时调用Get函数获取TIM3中断计数值作为起始计数值(StartTIM3Count);接着每次霍尔中断,都会将霍尔中断计数(HallCount)加1,直到HallCount == 120时,跳转到Calc状态。
3)在Calc状态中会获取TIM3中止计数值(StopTIM3Count),它和起始值相减就得到了120次霍尔中断所用的时间(单位是0.1ms);又因为电机转一周是12次霍尔中断(2对极),以及时间换算关系,就可以算出电机转速;计算完毕后立即跳回到Wait状态再进行一轮等待和计算;
4.3 代码生成及调用
将上面的模型生成代码后,代码文件数量都不变,直接拷贝到KEIL工程的路径下,并且也不用再修改原来的函数就可以编译了。
5 打印运行信息
在Main函数中加上一部分串口打印函数,可以看到电机运行时的ADC均值,PWM请求值、速度信息。
/* USER CODE BEGIN WHILE */
while (1)
{
printf("ADC1AverageValue:%d\r\n",ADC1AverageValue);
printf("PWMRequest:%d\r\n",PWMRequest);
printf("MotorSpeedHall:%d\r\n",MotorSpeedHall);
printf("=======================================\r\n");
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
程序下载到板子中,按下启动按钮,旋转电位器可以输出如下串口信息。
随着ADC1AverageValue增大,PWMRequest和MotorSpeedHall也相应地增大。
6 总结
这次BLDC控制实验实现了调速功能。但是由于是博主自己玩,也没有十分注意模型和代码地规范性和通用性,但愿很久之后自己也能看得懂了吧。