代码拉取完成,页面将自动刷新
/*
BLDC电机控制
*/
#include <stdio.h>
#include "main.h"
#include "stm32g4xx_ll_tim.h"
#include "bldc.h"
#include "hall.h"
#include "pid.h"
#include "adc.h"
#include "fdcan.h"
TIM_HandleTypeDef htim1;
extern uint16_t g_adc_val[ADC_CH_NUM];
bldc_ctrl_t bldc_ctrl = {
.motor_status = MOTOR_STOP,
.motor_direction = FORWARD_ROTATION,
.pwm_duty = 0
};
// 添加换向安全控制变量
static volatile uint8_t direction_change_pending = 0;
static volatile motor_direction_t pending_direction;
static volatile uint8_t zero_vector_cycles = 0;
#define ZERO_VECTOR_DELAY_CYCLES 3 // 零矢量延时周期数
_bldc_obj g_bldc_motor1 = {
.run_flag = MOTOR_STOP,
.dir = FORWARD_ROTATION,
.initial_calibration_done = 0 /* 初始校准默认为未完成 */
};
static uint8_t last_hall_value = 0;
uint32_t motor_timer_tick = 0;
static void calculate_motor_speed(uint8_t current_hall);
/*无刷电机正反转控制逻辑表。
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| 方向 | 霍尔 W | 霍尔 V | 霍尔 U | U+ | U- | V+ | V- | W+ | W- | 顺序 |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| | 1 | 0 | 1 | 打开 | 关闭 | 关闭 | 打开 | 关闭 | 关闭 | ↑ |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| | 0 | 0 | 1 | 打开 | 关闭 | 关闭 | 关闭 | 关闭 | 打开 | ↑ |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| 正转 | 0 | 1 | 1 | 关闭 | 关闭 | 打开 | 关闭 | 关闭 | 打开 | ↑ |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| | 0 | 1 | 0 | 关闭 | 打开 | 打开 | 关闭 | 关闭 | 关闭 | ↑ |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| | 1 | 1 | 0 | 关闭 | 打开 | 关闭 | 关闭 | 打开 | 关闭 | ↑ |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| | 1 | 0 | 0 | 关闭 | 关闭 | 关闭 | 打开 | 打开 | 关闭 | ↑ |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
|--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------|
| | 1 | 0 | 1 | 关闭 | 打开 | 打开 | 关闭 | 关闭 | 关闭 | ↓ |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| | 0 | 0 | 1 | 关闭 | 打开 | 关闭 | 关闭 | 打开 | 关闭 | ↓ |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| 反转 | 0 | 1 | 1 | 关闭 | 关闭 | 关闭 | 打开 | 打开 | 关闭 | ↓ |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| | 0 | 1 | 0 | 打开 | 关闭 | 关闭 | 打开 | 关闭 | 关闭 | ↓ |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| | 1 | 1 | 0 | 关闭 | 关闭 | 关闭 | 关闭 | 关闭 | 打开 | ↓ |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| | 1 | 0 | 0 | 关闭 | 关闭 | 打开 | 关闭 | 关闭 | 打开 | ↓ |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
*/
/* 六步换向函数指针数组 ,这个数组的函数顺序不是乱排的,它是根据电机正反转控制逻辑表来排的*/
//正转 4、6、2、3、1、5
motor_ctr_t pfunclist_m1[6] =
{
&m1_uhwl, &m1_vhul, &m1_vhwl,
&m1_whvl, &m1_uhvl, &m1_whul
};
/* bldc 电机初始化 */
void bldc_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
//拉低,不允许输出
HAL_GPIO_WritePin(GPIOC, MOTOR_OUTPUT_ENABLE_PIN, GPIO_PIN_RESET);
/*Configure GPIO pin : PC13 */
GPIO_InitStruct.Pin = MOTOR_OUTPUT_ENABLE_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
// 初始化定时器
bldc_timer_init();
// 初始化TIM2输入捕获测速
hall_init();
// 在启动前执行初始电流校准
printf("Performing initial current offset calibration...\r\n");
bldc_current_offset_calibration();
}
/* 定时器PWM初始化 */
void bldc_timer_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
// 使能时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_TIM1_CLK_ENABLE();
// 配置PWM输出引脚 (上桥臂)
GPIO_InitStruct.Pin = MOTOR_U_UP_PIN|MOTOR_V_UP_PIN|MOTOR_W_UP_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF6_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置互补PWM输出引脚 (下桥臂)
GPIO_InitStruct.Pin = MOTOR_U_LOW_PIN|MOTOR_V_LOW_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF6_TIM1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// 配置互补PWM输出引脚 (下桥臂)
//#NOTE 需要非常注意复用是 GPIO_AF4_TIM1, 这个要查询datasheet 的Alternate function表格
GPIO_InitStruct.Pin = MOTOR_W_LOW_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_TIM1; //-----注意这里
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 初始化GPIOB
// 定时器基本配置,pwm 周期为PWM_FREQUENCY_HZ 20k
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0; // 不分频,使用系统时钟
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = GET_PWM_PERIOD_VALUE() - 1; // PWM周期,20k 根据时钟动态计算
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = TIMER_REPETITION_COUNT-1; // 重复计数器,每TIMER_REPETITION_COUNT个PWM周期产生一次更新中断,提高响应频率到2kHz
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1; // 设置输出比较模式为PWM模式1。
sConfigOC.Pulse = 0; // 设置比较值(占空比)的初始值为0。
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // 设置主输出的有效电平极性为高电平。
// 设置互补输出的有效电平极性也为高电平,当OCPolarity为ON,则它为OFF,当OCPolarity为OFF,则它为ON,这里设置的是
//它为ON时的电平,所以这个是正确的,它也是TIM_OCNPOLARITY_HIGH
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; // 禁用快速模式,通常情况下不需要开启。
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; // 设置定时器在空闲模式(如停止或刹车)时,主输出引脚强制为低电平(RESET)。
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; // 设置定时器在空闲模式时,互补输出引脚也强制为低电平(RESET)。
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
// 配置PWM通道2 (V相)
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
// 配置PWM通道3 (W相)
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
printf("pwm Period: %d,pwm Duty: %d\r\n", htim1.Init.Period, sConfigOC.Pulse);
// 配置死区时间和刹车功能
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; // 运行模式下关断状态:立即设为非活动状态
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; // 空闲模式下关断状态:立即设为非活动状态
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_1; // 锁定级别1:防止意外修改死区时间等关键参数
sBreakDeadTimeConfig.DeadTime = (uint32_t)(GET_TIM1_CLOCK_FREQ() / 1000000000.0f * DEAD_TIME_NS); // 死区时间:根据配置和时钟动态计算
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; // 不启用刹车1:提供硬件级安全保护
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; // 刹车1极性:高电平有效
sBreakDeadTimeConfig.BreakFilter = 3; // 刹车1滤波:防止噪声误触发
sBreakDeadTimeConfig.Break2State = TIM_BREAK_DISABLE; // 不启用刹车2
sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH; // 刹车2极性:高电平有效
sBreakDeadTimeConfig.Break2Filter = 3; // 刹车2滤波:3级滤波,防止噪声误触发
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; // 禁用自动输出恢复:需要软件手动恢复
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
// #NOTEEnable (CCxE, CCxNE and OCxM) preload,它是实现同步配置输出的关键。
LL_TIM_CC_EnablePreload(TIM1);
// 配置NVIC中断优先级
HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn, 2, 0);
}
/**
* @brief U相上桥臂导通,V相下桥臂导通
* @param 无
* @retval 无
*/
void m1_uhvl(void)
{
// --- W 相: 高阻态 --- 禁用 W 相输出
TIM1->CCER &= ~(TIM_CCER_CC3E | TIM_CCER_CC3NE);
// --- U 相: PWM 输出 ---
TIM1->CCMR1 &= ~(TIM_CCMR1_OC1M_Msk); // 清除 U 相 (CH1) 的输出比较模式位
TIM1->CCMR1 |= (6U << TIM_CCMR1_OC1M_Pos); // 设置为 PWM1 模式 (0b110)
// TIM1->CCR1 = g_bldc_motor1.pwm_duty ;
TIM1->CCER |= (TIM_CCER_CC1E | TIM_CCER_CC1NE);
// --- V 相: 强制非活动电平 ---
TIM1->CCMR1 &= ~(TIM_CCMR1_OC2M_Msk); // 1. 清除 V 相 (CH2) 的输出比较模式位
TIM1->CCMR1 |= (4U << TIM_CCMR1_OC2M_Pos); // 2. 设置 V 相 (CH2) 为 "Force Inactive" 模式 (0b100)
TIM1->CCER |= (TIM_CCER_CC2E | TIM_CCER_CC2NE);// 3. 使能 V 相输出,让强制电平生效
}
/**
* @brief U相上桥臂导通,W相下桥臂导通
* @param 无
* @retval 无
*/
void m1_uhwl(void)
{
// --- V 相: 高阻态 ---
TIM1->CCER &= ~(TIM_CCER_CC2E | TIM_CCER_CC2NE); // 禁用 V 相输出
// --- U 相: PWM 输出 ---
TIM1->CCMR1 &= ~(TIM_CCMR1_OC1M_Msk); // 清除 U 相 (CH1) 的输出比较模式位
TIM1->CCMR1 |= (6U << TIM_CCMR1_OC1M_Pos); // 设置为 PWM1 模式 (0b110)
// TIM1->CCR1 = g_bldc_motor1.pwm_duty ;
TIM1->CCER |= (TIM_CCER_CC1E | TIM_CCER_CC1NE);
// --- W 相: 强制非活动电平 ---
TIM1->CCMR2 &= ~(TIM_CCMR2_OC3M_Msk); // 清除 W 相 (CH3) 的输出比较模式位
TIM1->CCMR2 |= (4U << TIM_CCMR2_OC3M_Pos); // 设置 W 相 (CH3) 为 "Force Inactive" 模式 (0b100)
TIM1->CCER |= (TIM_CCER_CC3E | TIM_CCER_CC3NE); // 使能 W 相输出,让强制电平生效
}
/**
* @brief V相上桥臂导通,W相下桥臂导通
* @param 无
* @retval 无
*/
void m1_vhwl(void)
{
// --- U 相: 高阻态 ---
TIM1->CCER &= ~(TIM_CCER_CC1E | TIM_CCER_CC1NE); // 禁用 U 相输出
// --- V 相: PWM 输出 ---
TIM1->CCMR1 &= ~(TIM_CCMR1_OC2M_Msk); // 清除 V 相 (CH2) 的输出比较模式位
TIM1->CCMR1 |= (6U << TIM_CCMR1_OC2M_Pos); // 设置为 PWM1 模式 (0b110)
// TIM1->CCR2 = g_bldc_motor1.pwm_duty ;
TIM1->CCER |= (TIM_CCER_CC2E | TIM_CCER_CC2NE);
// --- W 相: 强制非活动电平 ---
TIM1->CCMR2 &= ~(TIM_CCMR2_OC3M_Msk); // 清除 W 相 (CH3) 的输出比较模式位
TIM1->CCMR2 |= (4U << TIM_CCMR2_OC3M_Pos); // 设置 W 相 (CH3) 为 "Force Inactive" 模式 (0b100)
TIM1->CCER |= (TIM_CCER_CC3E | TIM_CCER_CC3NE); // 使能 W 相输出,让强制电平生效
}
/**
* @brief V相上桥臂导通,U相下桥臂导通
* @param 无
* @retval 无
*/
void m1_vhul(void)
{
// --- W 相: 高阻态 ---
TIM1->CCER &= ~(TIM_CCER_CC3E | TIM_CCER_CC3NE); // 禁用 W 相输出
// 设置 V 相 (CH2) 为 PWM1 模式
TIM1->CCMR1 &= ~(TIM_CCMR1_OC2M_Msk); // 清除 V 相 (CH2) 的输出比较模式位
TIM1->CCMR1 |= (6U << TIM_CCMR1_OC2M_Pos); // 设置为 PWM1 模式 (0b110)
// TIM1->CCR2 = g_bldc_motor1.pwm_duty ;
TIM1->CCER |= (TIM_CCER_CC2E | TIM_CCER_CC2NE);
// --- U 相: 强制非活动电平 ---
TIM1->CCMR1 &= ~(TIM_CCMR1_OC1M_Msk); // 清除 U 相 (CH1) 的输出比较模式位
TIM1->CCMR1 |= (4U << TIM_CCMR1_OC1M_Pos); // 设置 U 相 (CH1) 为 "Force Inactive" 模式 (0b100)
TIM1->CCER |= (TIM_CCER_CC1E | TIM_CCER_CC1NE); // 使能 U 相输出,让强制电平生效
}
/**
* @brief W相上桥臂导通,U相下桥臂导通
* @param 无
* @retval 无
*/
void m1_whul(void)
{
// --- V 相: 高阻态 ---
TIM1->CCER &= ~(TIM_CCER_CC2E | TIM_CCER_CC2NE); // 禁用 V 相输出
// --- W 相: PWM 输出 ---
TIM1->CCMR2 &= ~(TIM_CCMR2_OC3M_Msk); // 清除 W 相 (CH3) 的输出比较模式位
TIM1->CCMR2 |= (6U << TIM_CCMR2_OC3M_Pos); // 设置为 PWM1 模式 (0b110)
// TIM1->CCR3 = g_bldc_motor1.pwm_duty ;
TIM1->CCER |= (TIM_CCER_CC3E | TIM_CCER_CC3NE);
// --- U 相: 强制非活动电平 ---
TIM1->CCMR1 &= ~(TIM_CCMR1_OC1M_Msk); // 清除 U 相 (CH1) 的输出比较模式位
TIM1->CCMR1 |= (4U << TIM_CCMR1_OC1M_Pos); // 设置 U 相 (CH1) 为 "Force Inactive" 模式 (0b100)
TIM1->CCER |= (TIM_CCER_CC1E | TIM_CCER_CC1NE); // 使能 U 相输出,让强制电平生效
}
/**
* @brief W相上桥臂导通,V相下桥臂导通
* @param 无
* @retval 无
*/
void m1_whvl(void)
{
// --- U 相: 高阻态 ---
TIM1->CCER &= ~(TIM_CCER_CC1E | TIM_CCER_CC1NE); // 禁用 U 相输出
// --- W 相: PWM 输出 ---
TIM1->CCMR2 &= ~(TIM_CCMR2_OC3M_Msk); // 清除 W 相 (CH3) 的输出比较模式位
TIM1->CCMR2 |= (6U << TIM_CCMR2_OC3M_Pos); // 设置为 PWM1 模式 (0b110)
// TIM1->CCR3 = g_bldc_motor1.pwm_duty ;
TIM1->CCER |= (TIM_CCER_CC3E | TIM_CCER_CC3NE);
// --- V 相: 强制非活动电平 ---
TIM1->CCMR1 &= ~(TIM_CCMR1_OC2M_Msk); // 清除 V 相 (CH2) 的输出比较模式位
TIM1->CCMR1 |= (4U << TIM_CCMR1_OC2M_Pos); // 设置 V 相 (CH2) 为 "Force Inactive" 模式 (0b100)
TIM1->CCER |= (TIM_CCER_CC2E | TIM_CCER_CC2NE); // 使能 V 相输出,让强制电平生效
}
/* TIM1中断使能函数 */
void bldc_enable_timer_interrupt(void)
{
// 使能TIM1更新中断
__HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE);
HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);
printf("TIM1 interrupt enabled\r\n");
}
/* TIM1中断禁用函数 */
void bldc_disable_timer_interrupt(void)
{
// 禁用TIM1更新中断
__HAL_TIM_DISABLE_IT(&htim1, TIM_IT_UPDATE);
// 禁用NVIC中断
HAL_NVIC_DisableIRQ(TIM1_UP_TIM16_IRQn);
printf("TIM1 interrupt disabled\r\n");
}
/* 启动PWM输出 */
void bldc_start(void)
{
// 安全检查: 确保初始校准已完成
if (g_bldc_motor1.initial_calibration_done == 0)
{
printf("ERROR: Motor start failed. Initial current calibration is not complete.\r\n");
while(g_bldc_motor1.initial_calibration_done == 0)
{
HAL_Delay(10);
}
printf("Initial current calibration completed. Starting motor...\r\n");
}
//最后一次hall 设置为0,一个无效值,这样在start时current_hall!=last_hall_value,
//会触发一次换相操作,之后last_hall_value=current_hall,一切就开始了。
last_hall_value = 0;
pid_inter_data_reset();
// 启动TIM2输入捕获测速
bldc_speed_capture_start();
// 拉高,允许输出
HAL_GPIO_WritePin(MOTOR_OUTPUT_ENABLE_PORT, MOTOR_OUTPUT_ENABLE_PIN, GPIO_PIN_SET);
// 启动PWM通道1及其互补输出
if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
// 启动PWM通道2及其互补输出
if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
// 启动PWM通道3及其互补输出
if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
g_bldc_motor1.run_flag = MOTOR_RUN;
// 使能定时器中断
bldc_enable_timer_interrupt();
}
void bldc_stop(void)
{
uint8_t hall_value = 0;
bldc_disable_timer_interrupt();
// 停止TIM2输入捕获测速
bldc_speed_capture_stop();
// 拉低,不允许输出
HAL_GPIO_WritePin(MOTOR_OUTPUT_ENABLE_PORT, MOTOR_OUTPUT_ENABLE_PIN, GPIO_PIN_RESET);
// 立即清零CCR寄存器的预装载值
htim1.Instance->CCR1 = 0;
htim1.Instance->CCR2 = 0;
htim1.Instance->CCR3 = 0;
// 停止PWM输出及其互补输出
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_2);
HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_2);
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_3);
HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_3);
// #NOTE强制产生更新事件,立即将CCR预装载值传输到实际比较寄存器,否则就很有可能产生一种情况
//就是timer stop时CCR1设置为0了,但实际它还没更新到实际比较器,在start 后会跑到一个更新事件
//才更新,导致在start时,PWM输出不是0占空比,导致上下臂直通,烧电机
TIM1->EGR = TIM_EGR_COMG | TIM_EGR_UG;
// 等待更新事件完成
while(!(htim1.Instance->SR & TIM_SR_UIF));
htim1.Instance->SR &= ~TIM_SR_UIF; // 清除更新中断标志
//motor 本身变量 reset
g_bldc_motor1.run_flag = MOTOR_STOP;
g_bldc_motor1.speed = 0;
g_bldc_motor1.pwm_duty = 0;
//测速 计算相关变量 reset
motor_timer_tick = 0;
pid_init();//init 也是reset
pid_inter_data_reset();
fdcan_clear_direction_change_request();
//debug
hall_value = read_hall_sensors();
printf("bldc_stop,hall: %d\r\n",hall_value);
// // 在电机完全停止后执行重新校准
// printf("Performing current offset re-calibration...\r\n");
// bldc_current_offset_calibration();
}
void bldc_set_pwm_duty(uint32_t duty)
{
if(duty > 6000)
{
duty = 6000;
printf("duty > 6000, set duty to 6000\r\n");
}
g_bldc_motor1.pwm_duty = duty;
printf("duty: ----%d\r\n", duty);
}
void bldc_set_motor_direction(motor_direction_t direction)
{
if(direction == FORWARD_ROTATION || direction == REVERSE_ROTATION)
{
g_bldc_motor1.dir= direction;
// printf("d:%d\r\n", direction);
}
else
{
printf("direction error: ---- %d\r\n", direction);
}
}
uint8_t bldc_get_motor_direction(void)
{
return g_bldc_motor1.dir;
}
uint8_t bldc_get_motor_status(void)
{
return g_bldc_motor1.run_flag;
}
/* 调试函数:打印时钟信息 */
void bldc_print_clock_info(void)
{
uint32_t hclk_freq = HAL_RCC_GetHCLKFreq();
uint32_t pclk2_freq = HAL_RCC_GetPCLK2Freq();
uint32_t tim1_clock = GET_TIM1_CLOCK_FREQ();
uint32_t pwm_period = GET_PWM_PERIOD_VALUE();
uint32_t actual_pwm_freq = tim1_clock / pwm_period;
printf("=== clock info ===\r\n");
printf("HCLK frequency: %u Hz\r\n", hclk_freq);
printf("PCLK2 frequency: %u Hz\r\n", pclk2_freq);
printf("TIM1 clock frequency: %u Hz\r\n", tim1_clock);
printf("PWM period value(ARR): %u\r\n", pwm_period);
printf("actual PWM frequency: %u Hz\r\n", actual_pwm_freq);
printf("target PWM frequency: %d Hz\r\n", PWM_FREQUENCY_HZ);
printf("RPM_CALC_CONSTANT: %llu\r\n", RPM_CALC_CONSTANT);
printf("=====================\r\n");
}
//100us 执行一次
void bldc_timer_interrupt_callback(void)
{
int32_t pid_duty = 0;
// 更新系统滴答计数
uint8_t current_hall = read_hall_sensors();
if(current_hall == 0b000 || current_hall == 0b111)
{
printf("hall not valid %d",current_hall);
return;
}
if(motor_timer_tick % 32 == 0)
{
bldc_get_motor_rpm_from_capture();
motor_direction_t new_direction;
uint32_t new_speed;
// 检查是否有反转的命令,要反转需要速度降到100以下,之后再切换方向反转
if (fdcan_get_direction_change_request(&new_direction, &new_speed))
{
// 检查速度是否已经足够低
if (g_bldc_motor1.speed <= 100)
{
pid_reset_integral(); // 清除PID积分误差
bldc_set_motor_direction(new_direction); // 设置新方向
pid_set_target_value(new_speed); // 设置新速度
fdcan_clear_direction_change_request(); // 清除切换请求
// printf("Direction changed to %d, new speed: %d\r\n", new_direction, new_speed);
}
}
pid_duty = pid_ctrl(&g_speed_pid, g_bldc_motor1.speed);
g_bldc_motor1.pwm_duty = pid_duty;
TIM1->CCR1 = g_bldc_motor1.pwm_duty ;
TIM1->CCR2 = g_bldc_motor1.pwm_duty ;
TIM1->CCR3 = g_bldc_motor1.pwm_duty ;
// printf("%d\t",g_bldc_motor1.pwm_duty);
bldc_get_phase_currents(&g_bldc_motor1.current_u, &g_bldc_motor1.current_v, &g_bldc_motor1.current_w);
}
if(current_hall!=last_hall_value)
{
// printf("hall: %d\r\n",current_hall);
if( g_bldc_motor1.dir== FORWARD_ROTATION)
{
pfunclist_m1[current_hall-1]();// 正转 4、6、2、3、1、5
}
else
{
pfunclist_m1[6-current_hall](); // 反转
}
LL_TIM_GenerateEvent_COM(TIM1);// 产生更新事件,同时更新改变的几个寄存器
last_hall_value = current_hall;
}
motor_timer_tick++;
}
/**
* @brief 对三相电流进行零点偏置校准
* @note 此函数应在电机完全静止时调用.
* 它会采样多次ADC读数并计算平均值作为零点偏置.
* 如果在此过程中电机启动, 校准会自动中止.
* @param 无
* @retval 无
*/
void bldc_current_offset_calibration(void)
{
uint32_t i;
uint32_t offset_sum_u = 0;
uint32_t offset_sum_v = 0;
uint32_t offset_sum_w = 0;
// 确保在进入校准前,状态为停止
if (g_bldc_motor1.run_flag != MOTOR_STOP)
{
printf("ERROR: Cannot calibrate while motor is running.\r\n");
return;
}
for (i = 0; i < CURRENT_OFFSET_CALIBRATION_SAMPLES; i++)
{
// 检查电机是否在中途启动
if (g_bldc_motor1.run_flag != MOTOR_STOP)
{
printf("WARNING: Calibration aborted due to motor start request.\r\n");
return; // 中止校准,保留旧的偏置值
}
// 累加ADC读数
// g_adc_val 是在DMA中断中被更新的,包含了经过初步平均的值
offset_sum_u += g_adc_val[2]; // U相电流在ADC数组中的索引是2
offset_sum_v += g_adc_val[3]; // V相电流在ADC数组中的索引是3
offset_sum_w += g_adc_val[4]; // W相电流在ADC数组中的索引是4
HAL_Delay(1); // 等待下一次DMA转换和平均计算完成
}
// 计算平均值并存储
g_bldc_motor1.adc_offset_u = offset_sum_u / CURRENT_OFFSET_CALIBRATION_SAMPLES;
g_bldc_motor1.adc_offset_v = offset_sum_v / CURRENT_OFFSET_CALIBRATION_SAMPLES;
g_bldc_motor1.adc_offset_w = offset_sum_w / CURRENT_OFFSET_CALIBRATION_SAMPLES;
// 标记初始校准已成功完成
if (g_bldc_motor1.initial_calibration_done == 0)
{
g_bldc_motor1.initial_calibration_done = 1;
}
printf("Calibration complete. Offsets: U=%d, V=%d, W=%d\r\n",
g_bldc_motor1.adc_offset_u,
g_bldc_motor1.adc_offset_v,
g_bldc_motor1.adc_offset_w);
}
/**
* @brief 获取经过零点校准后的三相电流值
* @param i_u: 指向U相电流值(A)存储位置的指针
* @param i_v: 指向V相电流值(A)存储位置的指针
* @param i_w: 指向W相电流值(A)存储位置的指针
* @retval 无
*/
void bldc_get_phase_currents(volatile float *i_u, volatile float *i_v, volatile float *i_w)
{
int16_t corrected_adc_u;
int16_t corrected_adc_v;
int16_t corrected_adc_w;
// 读取瞬时ADC值
// g_adc_val 数组由 adc.c 中的DMA中断回调函数持续更新
uint16_t raw_adc_u = g_adc_val[2];
uint16_t raw_adc_v = g_adc_val[3];
uint16_t raw_adc_w = g_adc_val[4];
// 减去偏置值
corrected_adc_u = raw_adc_u - g_bldc_motor1.adc_offset_u;
corrected_adc_v = raw_adc_v - g_bldc_motor1.adc_offset_v;
corrected_adc_w = raw_adc_w - g_bldc_motor1.adc_offset_w;
// 转换为实际电流值 (Ampere)
if (i_u) *i_u = (float)corrected_adc_u * ADC2CURT;
if (i_v) *i_v = (float)corrected_adc_v * ADC2CURT;
if (i_w) *i_w = (float)corrected_adc_w * ADC2CURT;
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。