特性:实现电机控制与霍尔传感器功能

新增电机板级支持包:基于 AT8236-MS 驱动芯片,实现了对 4 个直流电机的 PWM 控制。
实现霍尔传感器功能:用于速度测量和脉冲计数。
更新 GPIO 初始化:为霍尔传感器添加了外部中断(EXTI)配置。
修改系统时钟配置:改用高速外部时钟(HSE)并调整了锁相环(PLL)设置。
更改定时器配置:将时基生成从 TIM8 改为 TIM4。
增强 FreeRTOS 任务:实现从霍尔传感器周期性读取并更新速度数据。
更新项目配置:以反映外设使用情况和优先级的变更。
This commit is contained in:
2026-04-03 20:24:55 +08:00
parent a53aa38ed3
commit 1cc8327f13
17 changed files with 682 additions and 149 deletions

99
Core/Bsp/README.md Normal file
View File

@@ -0,0 +1,99 @@
# BSP Motor 驱动说明 (AT8236-MS)
该驱动模块用于通过 STM32 的硬件 PWM 功能控制 4 路直流电机,适配 **AT8236-MS** 驱动芯片。
## 硬件连接
| 电机 ID | 信号定义 | STM32 引脚 | 定时器通道 |
| :--- | :--- | :--- | :--- |
| **MOTOR_1** | M1_W / M1_S | PE9 / PE11 | TIM1_CH1 / TIM1_CH2 |
| **MOTOR_2** | M2_W / M2_S | PE13 / PE14 | TIM1_CH3 / TIM1_CH4 |
| **MOTOR_3** | M3_W / M3_S | PA15 / PB3 | TIM2_CH1 / TIM2_CH2 |
| **MOTOR_4** | M4_W / M4_S | PA2 / PA3 | TIM2_CH3 / TIM2_CH4 |
## 霍尔传感器 (由于是一根线,仅限频率测速)
| 传感器 ID | STM32 引脚 | 模式 | 功能 |
| :--- | :--- | :--- | :--- |
| **HALL_1 (M1)** | PD9 (M_I_1) | GPIO_EXTI | 脉冲计数 / 测速 |
| **HALL_2 (M2)** | PD8 (M_I_2) | GPIO_EXTI | 脉冲计数 / 测速 |
| **HALL_3 (M3)** | PB15 (M_I_3) | GPIO_EXTI | 脉冲计数 / 测速 |
| **HALL_4 (M4)** | PB14 (M_I_4) | GPIO_EXTI | 脉冲计数 / 测速 |
## 使用方式
### 1. 硬件初始化 (main.c)
`main.c` 的循环前调用电机和霍尔的初始化函数:
```c
/* USER CODE BEGIN 2 */
motor_init();
hall_init();
/* USER CODE END 2 */
```
### 2. 中断绑定 (stm32f1xx_it.c)
在外部中断回调函数中加入脉冲统计:
```c
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
hall_pulse_callback(GPIO_Pin);
}
```
### 3. 控制电机
调用 `motor_set_speed` 函数控制电机转动:
```c
// 电机1正转中等速度
motor_set_speed(MOTOR_1, 2000);
// 电机2反转全速
motor_set_speed(MOTOR_2, -3599);
```
### 4. 获取测量结果
在 FreeRTOS 任务或主循环中读取脉冲数或计算转速:
```c
uint32_t count = hall_get_count(MOTOR_1);
printf("Motor 1 Pulses: %ld\r\n", count);
```
## 预期结果
1. **电机控制**
- 给予正数速度时,对应电机的 W 引脚输出 PWMS 引脚为低,电机正向旋转。
- 给予负数速度时S 引脚输出 PWMW 引脚为低,电机反向旋转。
- 速度数值越大,电机转速越高,最大有效值为 3599。
2. **霍尔测量**
- 电机每旋转一圈,对应的 `pulse_count` 会根据编码器磁极对数增加。
- 通过观察 `hall_get_count` 的数值增长,可以确认传感器信号捕获正常。
## 驱动逻辑 (AT8236-MS)
驱动器通过两路电平差控制方向:
- **正转**: W引脚输出 PWMS引脚输出低电平(0%)。
- **反转**: W引脚输出低电平(0%)S引脚输出 PWM。
- **停止/刹车**: W和S引脚均输出低电平(0%)。
## 核心 API
### 1. 初始化
`void motor_init(void);`
- 开启 TIM1 和 TIM2 的所有对应 PWM 通道。
- 初始电机状态为停止。
### 2. 设置电机速度
`void motor_set_speed(motor_id_t motor_id, int16_t speed);`
- **motor_id**: `MOTOR_1``MOTOR_4`
- **speed**: 范围为 `-3599``3599`(对应 20kHz 频率下的 ARR 重装载值)。
- 正数:正转。
- 负数:反转。
- 0停止。
### 3. 停止电机
`void motor_stop(motor_id_t motor_id);`
- 立即将对应电机的两路占空比清零。
## 注意事项
- 使用前请确保 `MX_TIM1_Init()``MX_TIM2_Init()` 已在 `main.c` 中正常调用。
- 定时器的 `Period`(重装载值值)决定了最大速度的分辨率,当前配置为 16 位65535

134
Core/Bsp/bsp_hall.c Normal file
View File

@@ -0,0 +1,134 @@
#include "bsp_hall.h"
/* 霍尔传感器数据结构体定义描述 */
static hall_sensor_t sensors[MOTOR_COUNT];
/**
* @brief 霍尔引脚初始化 (本驱动依赖 CubeMX 预设的 GPIO_EXTI 模式)
*/
void hall_init(void)
{
for(int i = 0; i < MOTOR_COUNT; i++) {
sensors[i].pulse_count = 0;
sensors[i].speed_rpm = 0.0f;
}
}
/**
* @brief HAL 层 GPIO 外部中断回调函数实现
* @note 此函数重写了 HAL 库中的 __weak 定义
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
hall_pulse_callback(GPIO_Pin);
}
/**
* @brief 外部中断回调函数中的统计处理
*/
void hall_pulse_callback(uint16_t GPIO_Pin)
{
/* 只负责计数,不再在中断中进行浮点运算和日志打印 */
if (GPIO_Pin == M_I_1_Pin) {
sensors[MOTOR_1].pulse_count++;
} else if (GPIO_Pin == M_I_2_Pin) {
sensors[MOTOR_2].pulse_count++;
} else if (GPIO_Pin == M_I_3_Pin) {
sensors[MOTOR_3].pulse_count++;
} else if (GPIO_Pin == M_I_4_Pin) {
sensors[MOTOR_4].pulse_count++;
}
}
// /**
// * @brief 获取霍尔传感器脉冲计数
// */
// uint32_t hall_get_count(motor_id_t motor_id)
// {
// if (motor_id < MOTOR_COUNT) {
// return sensors[motor_id].pulse_count;
// }
// return 0;
// }
// /**
// * @brief 重置霍尔传感器计数值
// */
// void hall_reset_count(motor_id_t motor_id)
// {
// if (motor_id < MOTOR_COUNT) {
// sensors[motor_id].pulse_count = 0;
// }
// }
/**
* @brief 计算并更新转速 (供应用层在定时任务中调用)
* @param motor_id 电机 ID
* @param interval_ms 两次调用之间的时间间隔 (ms)
* @return float 计算出的 RPM
*/
float hall_update_speed(motor_id_t motor_id, uint32_t interval_ms)
{
if (motor_id >= MOTOR_COUNT || interval_ms == 0) return 0.0f;
/* 获取当前增量并重置计数 */
uint32_t current_pulses = sensors[motor_id].pulse_count;
sensors[motor_id].pulse_count = 0;
/* PPR = 330. RPM = (增量脉冲 / 间隔ms) * 60000ms / PPR */
sensors[motor_id].speed_rpm = ((float)current_pulses / (float)interval_ms) * 60000.0f / 330.0f;
// /* 应用层打印日志 */
// log_i("Motor[%d] Real-time: %.2f RPM (pulses: %d)", motor_id + 1, sensors[motor_id].speed_rpm, current_pulses);
return sensors[motor_id].speed_rpm;
}
// /**
// * @brief 计算转速 (由于只有一根线,仅支持根据脉冲频率计算速度)
// */
// float hall_calculate_speed(motor_id_t motor_id, uint32_t interval_ms)
// {
// if (motor_id >= MOTOR_COUNT || interval_ms == 0) return 0.0f;
//
// /* 获取当前脉冲数 */
// uint32_t current_count = sensors[motor_id].pulse_count;
//
// /*
// * 计算 RPM (转每分钟)
// * 公式: (脉冲增量 / 采样时间ms) * 1000ms * 60s / PPR
// * 注意:由于此处没有传入上一次的 count我们假设 interval_ms 是相对于 count=0 开始的,
// * 或者你可以在调用前手动 reset。这里先实现基础频率转换为 RPM 的逻辑。
// * 假设 PPR (每圈脉冲数) 暂定为 11 (常见磁平衡霍尔) * 30 (减速比) = 330
// */
// const float PPR = 330.0f;
// sensors[motor_id].speed_rpm = ((float)current_count / interval_ms) * 60000.0f / PPR;
//
// /* 输出日志:电机编号 + 转速 */
// // log_i("Motor[%d] speed: %.2f RPM", motor_id + 1, sensors[motor_id].speed_rpm);
//
// /* 计算完后为了下一次增量计算,通常需要重置计数,或者记录旧值。这里保持框架。 */
//
// return sensors[motor_id].speed_rpm;
// }
/* USER CODE BEGIN Header_speed_get */
/**
* @brief Function implementing the timerTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_speed_get */
void speed_get(void *argument) {
/* USER CODE BEGIN speed_get */
/* Infinite loop */
for (;;) {
/* 在每 100ms 执行一次的任务中 */
for (int i = 0; i < MOTOR_COUNT; i++) {
hall_update_speed(i, 100);
}
osDelay(100); // 100ms
}
/* USER CODE END speed_get */
}

35
Core/Bsp/bsp_hall.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef __BSP_HALL_H
#define __BSP_HALL_H
#include "main.h"
#include "bsp_motor.h" // 确保 motor_id_t 类型可见
#include "elog.h"
#include "cmsis_os.h"
/**
* @brief 霍尔周期/脉冲计数结构体
*/
typedef struct {
uint32_t pulse_count; // 总脉冲计数值
float speed_rpm; // 计算得到的转速 (RPM)
} hall_sensor_t;
/* 初始化霍尔传感器 (GPIO 外部中断模式) */
void hall_init(void);
/* 外部中断处理接口 (供 HAL 层调用,请勿在应用层直接调用) */
void hall_pulse_callback(uint16_t GPIO_Pin);
// /* 获取指定霍尔传感器的计数值 */
// uint32_t hall_get_count(motor_id_t motor_id);
/* 每隔固定时间调用此函数计算并更新转速 (例如在 100ms 任务中调用) */
float hall_update_speed(motor_id_t motor_id, uint32_t interval_ms);
// /* 清位计数值 */
// void hall_reset_count(motor_id_t motor_id);
// /* 计算转速的原始接口 (内部使用) */
// float hall_calculate_speed(motor_id_t motor_id, uint32_t interval_ms);
#endif /* __BSP_HALL_H */

122
Core/Bsp/bsp_motor.c Normal file
View File

@@ -0,0 +1,122 @@
#include "bsp_motor.h"
#include "tim.h"
/**
* @file bsp_motor.c
* @brief AT8236-MS 电机驱动实现 (PWM)
*
* AT8236-MS 驱动逻辑参考:
* - IN1 (PWM) / IN2 (L) -> 正转
* - IN1 (L) / IN2 (PWM) -> 反转
* - IN1 (L) / IN2 (L) -> 停止/刹车 (视具体芯片配置而定)
*/
/**
* @brief 初始化电机相关外设
*/
void motor_init(void)
{
/* 1. 开启 PWM 定时器硬件通道 (TIM1 用于 M1, M2) */
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);
/* --- 关键修改:强制使能 TIM1 的主输出 (仅高级定时器需要) --- */
__HAL_TIM_MOE_ENABLE(&htim1);
/* 2. 开启 PWM 定时器硬件通道 (TIM2 用于 M3, M4) */
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
/* 3. 初始状态设置为停止 */
motor_stop(MOTOR_1);
motor_stop(MOTOR_2);
motor_stop(MOTOR_3);
motor_stop(MOTOR_4);
}
/**
* @brief 设置电机PWM速度和方向
* @param motor_id 电机 ID (MOTOR_1 到 MOTOR_4)
* @param speed 速度值 (-3599 到 3599正数正转负数反转)
*/
void motor_set_speed(motor_id_t motor_id, int16_t speed)
{
TIM_HandleTypeDef* htim_w = NULL;
uint32_t channel_w = 0;
TIM_HandleTypeDef* htim_s = NULL;
uint32_t channel_s = 0;
/* 获取对应电机的 定时器句柄和通道 (根据 tim.c GPIO 复用定义) */
switch (motor_id) {
case MOTOR_1: // PE9->TIM1_CH1, PE11->TIM1_CH2
htim_w = &htim1; channel_w = TIM_CHANNEL_1;
htim_s = &htim1; channel_s = TIM_CHANNEL_2;
break;
case MOTOR_2: // PE13->TIM1_CH3, PE14->TIM1_CH4
htim_w = &htim1; channel_w = TIM_CHANNEL_3;
htim_s = &htim1; channel_s = TIM_CHANNEL_4;
break;
case MOTOR_3: // PA15->TIM2_CH1, PB3->TIM2_CH2
htim_w = &htim2; channel_w = TIM_CHANNEL_1;
htim_s = &htim2; channel_s = TIM_CHANNEL_2;
break;
case MOTOR_4: // PA2->TIM2_CH3, PA3->TIM2_CH4
htim_w = &htim2; channel_w = TIM_CHANNEL_3;
htim_s = &htim2; channel_s = TIM_CHANNEL_4;
break;
default: return;
}
/* 限制速度范围 (ARR=3599) */
if (speed > 3599) speed = 3599;
if (speed < -3599) speed = -3599;
/* 计算绝对值速度 */
uint16_t pwm_val = (speed < 0) ? (uint16_t)(-speed) : (uint16_t)speed;
/* 根据速度判断方向 */
if (speed > 0) {
/* 正转W 输出 PWM, S 输出低电平 (占空比 0) */
__HAL_TIM_SET_COMPARE(htim_w, channel_w, pwm_val);
__HAL_TIM_SET_COMPARE(htim_s, channel_s, 0);
} else if (speed < 0) {
/* 反转W 输出低电平 (占空比 0), S 输出 PWM */
__HAL_TIM_SET_COMPARE(htim_w, channel_w, 0);
__HAL_TIM_SET_COMPARE(htim_s, channel_s, pwm_val);
} else {
motor_stop(motor_id);
}
}
/**
* @brief 停止电机
* @param motor_id 电机 ID
*/
void motor_stop(motor_id_t motor_id)
{
/* 设置对应通道占空比均为 0 以刹车/停止 */
switch (motor_id) {
case MOTOR_1:
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 0);
break;
case MOTOR_2:
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, 0);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, 0);
break;
case MOTOR_3:
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 0);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, 0);
break;
case MOTOR_4:
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, 0);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, 0);
break;
default: break;
}
}

35
Core/Bsp/bsp_motor.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef __BSP_MOTOR_H
#define __BSP_MOTOR_H
#include "main.h"
/**
* @brief 电机选择枚举
*/
typedef enum {
MOTOR_1 = 0,
MOTOR_2,
MOTOR_3,
MOTOR_4,
MOTOR_COUNT
} motor_id_t;
/**
* @brief 电机方向枚举
*/
typedef enum {
MOTOR_FWD = 0, // 正转
MOTOR_REV, // 反转
MOTOR_STOP // 停止
} motor_dir_t;
/* 电机初始化 */
void motor_init(void);
/* 设置电机PWM占空比和方向 */
void motor_set_speed(motor_id_t motor_id, int16_t speed);
/* 停止指定电机 */
void motor_stop(motor_id_t motor_id);
#endif /* __BSP_MOTOR_H */