编程技术分享

分享编程知识,探讨技术创新

0%

简介:重磅推出!自制odrive3.5V ,24V/30A!

好的,作为一名高级嵌入式软件开发工程师,很高兴能和你一起探讨这个自制ODrive 3.5V,24V/30A项目。从你提供的图片和简介来看,这是一个非常具有挑战性和实用价值的嵌入式项目,它涵盖了嵌入式系统开发的多个关键环节。为了构建一个可靠、高效、可扩展的系统平台,我们需要深入分析需求,精心设计架构,并采用经过实践验证的技术和方法。
关注微信公众号,提前获取相关推文

下面,我将从需求分析、系统架构设计、关键技术选择、详细代码实现(C语言),测试验证以及维护升级等方面,为你详细阐述最适合这个项目的代码设计架构,并提供具体的C代码示例。

1. 需求分析

首先,我们需要深入理解这个自制ODrive 3.5V,24V/30A项目的具体需求。基于图片和简介,我们可以初步分析出以下核心需求:

  • 电机驱动: 核心功能是驱动无刷直流电机 (BLDC) 或永磁同步电机 (PMSM)。从图片上的DRV8302芯片来看,这是一款常用的三相电机驱动芯片,进一步印证了电机驱动的需求。
  • 电压和电流: 需要支持24V电压输入,并提供高达30A的持续电流输出能力。这决定了功率器件的选型和散热设计。
  • 控制方式: ODrive以高性能电机控制著称,因此,我们应该考虑实现先进的电机控制算法,例如磁场定向控制 (FOC),以实现精确、高效的电机控制。
  • 反馈系统: 为了实现闭环控制,需要使用电机编码器或霍尔传感器等反馈器件,获取电机的位置和速度信息。图片上可以看到编码器接口。
  • 通信接口: 需要提供通信接口,以便上位机或控制器能够发送指令,读取状态信息,并进行参数配置。常见的接口包括USB、CAN、UART等。图片上可以看到USB接口。
  • 保护功能: 为了保证系统的安全可靠运行,需要实现过流保护、过压保护、过温保护等功能。
  • 可扩展性: 系统架构需要具有良好的可扩展性,方便后续添加新功能或支持更多类型的电机和应用场景。
  • 可靠性: 嵌入式系统必须具备高可靠性,能够在各种工况下稳定运行。
  • 高效性: 系统设计要注重效率,降低功耗,提高能量利用率。
  • 维护升级: 系统需要支持固件升级,方便bug修复和功能更新。

2. 系统架构设计

为了满足上述需求,并构建可靠、高效、可扩展的系统平台,我建议采用分层架构的设计模式。这种架构将系统划分为多个独立的层次,每个层次负责特定的功能,层次之间通过清晰定义的接口进行通信。

2.1 软件架构分层

我们可以将软件架构分为以下几个层次:

  • 应用层 (Application Layer): 负责处理用户指令、实现高级功能逻辑、系统状态管理等。例如,接收上位机指令,解析指令参数,调用下层接口执行电机控制,并向上位机反馈运行状态。
  • 电机控制层 (Motor Control Layer): 实现核心的电机控制算法,例如FOC、PID控制、空间矢量调制 (SVM) 等。该层接收来自应用层的控制指令,并根据电机反馈信息计算出合适的PWM信号,驱动电机运行。
  • 驱动层 (Driver Layer): 负责驱动和控制硬件外设,例如DRV8302电机驱动芯片、编码器接口、PWM发生器、ADC采样器、通信接口等。驱动层向上层提供统一的硬件访问接口,隐藏底层硬件细节。
  • 硬件抽象层 (HAL - Hardware Abstraction Layer): 对底层硬件进行进一步抽象,提供与硬件平台无关的接口。HAL层可以屏蔽不同硬件平台的差异,提高代码的可移植性。例如,定义通用的GPIO、PWM、ADC、Timer等接口,然后在不同的硬件平台上实现这些接口。
  • 底层硬件层 (Hardware Layer): 这是最底层,直接与硬件交互。通常由芯片厂商提供的库函数或寄存器操作组成。

2.2 架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
+---------------------+
| 应用层 (Application Layer) | 例如:指令解析、状态管理、通信协议处理
+---------------------+
|
| 控制指令和状态反馈
V
+---------------------+
| 电机控制层 (Motor Control Layer) | 例如:FOC算法、PID控制、SVM
+---------------------+
|
| PWM信号、控制参数、反馈数据
V
+---------------------+
| 驱动层 (Driver Layer) | 例如:DRV8302驱动、编码器驱动、PWM驱动、ADC驱动、通信驱动
+---------------------+
|
| 硬件访问接口 (GPIO, PWM, ADC, SPI/I2C/UART, Timer...)
V
+---------------------+
| 硬件抽象层 (HAL - Hardware Abstraction Layer) | 例如:通用GPIO接口、通用PWM接口、通用ADC接口
+---------------------+
|
| 硬件平台相关的库函数或寄存器操作
V
+---------------------+
| 底层硬件层 (Hardware Layer) | 例如:STM32芯片、DRV8302芯片、编码器、传感器
+---------------------+

3. 关键技术选择

在这个项目中,我们需要选择合适的技术和方法来保证系统的性能和可靠性。以下是一些关键技术的选择:

  • 微控制器 (MCU): 选择高性能的MCU是至关重要的。考虑到ODrive的性能要求和FOC算法的计算量,建议选择具有浮点运算单元 (FPU) 的32位ARM Cortex-M4或更高级别的MCU,例如STM32F4系列、STM32F7系列、STM32H7系列等。STM32系列MCU生态完善,开发资料丰富,非常适合嵌入式开发。
  • 电机驱动芯片: DRV8302是一款集成了栅极驱动器、电流检测放大器和保护功能的芯片,能够简化外围电路设计,提高系统可靠性。我们可以充分利用DRV8302的特性。
  • 电机控制算法: 磁场定向控制 (FOC) 是高性能电机控制的首选算法。FOC能够实现对电机磁场和转矩的精确控制,从而获得更好的动态性能和效率。
  • 编码器: 增量式编码器是常用的电机位置反馈器件,具有精度高、成本适中的优点。我们需要选择合适的编码器分辨率,并设计可靠的编码器接口电路。
  • PWM发生器: MCU的PWM模块需要能够生成高分辨率、高频率的PWM信号,以驱动DRV8302控制电机。高级定时器 (TIM1, TIM8等) 通常具有互补PWM输出和死区时间控制功能,非常适合电机控制应用。
  • ADC采样器: 为了实现FOC算法,我们需要实时采样电机的相电流和母线电压。MCU的ADC模块需要具有足够高的采样速率和精度。
  • 通信接口: USB接口方便进行调试和固件升级。CAN或UART接口可以用于与上位机或控制器进行实时通信。根据具体的应用场景选择合适的通信接口。
  • RTOS (可选): 对于复杂的电机控制系统,可以考虑使用实时操作系统 (RTOS),例如FreeRTOS、RT-Thread等。RTOS能够提高系统的实时性、并发性和可维护性。

4. 详细代码实现 (C语言)

下面,我将提供一些关键模块的C代码示例,以展示系统架构的具体实现。由于代码量较大,我将重点展示HAL层、驱动层、电机控制层和应用层的一些核心代码片段,并进行详细的注释和解释。

4.1 HAL层 (Hardware Abstraction Layer)

HAL层主要定义了与硬件平台无关的接口,例如GPIO、PWM、ADC、Timer等。

HAL_gpio.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

#include <stdint.h>
#include <stdbool.h>

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
GPIO_MODE_AF // Alternate Function
} GPIO_ModeTypeDef;

typedef enum {
GPIO_PULL_NONE,
GPIO_PULL_UP,
GPIO_PULL_DOWN
} GPIO_PullTypeDef;

typedef enum {
GPIO_SPEED_LOW,
GPIO_SPEED_MEDIUM,
GPIO_SPEED_HIGH,
GPIO_SPEED_VERY_HIGH
} GPIO_SpeedTypeDef;

typedef struct {
uint32_t Pin; // GPIO Pin
GPIO_ModeTypeDef Mode; // GPIO Mode
GPIO_PullTypeDef Pull; // Pull-up/Pull-down resistor
GPIO_SpeedTypeDef Speed; // GPIO Speed
} GPIO_InitTypeDef;

// 初始化GPIO
void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct);

// 设置GPIO输出电平
void HAL_GPIO_WritePin(uint32_t Pin, bool PinState);

// 读取GPIO输入电平
bool HAL_GPIO_ReadPin(uint32_t Pin);

#endif // HAL_GPIO_H

HAL_gpio.c (示例,基于STM32):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "HAL_gpio.h"
#include "stm32f4xx_hal.h" // 引入STM32 HAL库

void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct) {
GPIO_InitTypeDef GPIO_Config;
GPIO_Config.Pin = GPIO_InitStruct->Pin;
GPIO_Config.Mode = (GPIO_InitStruct->Mode == GPIO_MODE_INPUT) ? GPIO_MODE_INPUT :
(GPIO_InitStruct->Mode == GPIO_MODE_OUTPUT) ? GPIO_MODE_OUTPUT_PP : GPIO_MODE_AF_PP; // 简化示例,只考虑推挽输出和输入
GPIO_Config.Pull = (GPIO_InitStruct->Pull == GPIO_PULL_NONE) ? GPIO_NOPULL :
(GPIO_InitStruct->Pull == GPIO_PULL_UP) ? GPIO_PULLUP : GPIO_PULLDOWN;
GPIO_Config.Speed = (GPIO_InitStruct->Speed == GPIO_SPEED_LOW) ? GPIO_SPEED_FREQ_LOW :
(GPIO_InitStruct->Speed == GPIO_SPEED_MEDIUM) ? GPIO_SPEED_FREQ_MEDIUM :
(GPIO_InitStruct->Speed == GPIO_SPEED_HIGH) ? GPIO_SPEED_FREQ_HIGH : GPIO_SPEED_FREQ_VERY_HIGH;

// 这里需要根据具体的STM32硬件平台进行GPIO端口和时钟使能配置,这里省略
HAL_GPIO_Init(GPIOA, &GPIO_Config); // 假设都使用GPIOA,实际需要根据Pin参数判断端口
}

void HAL_GPIO_WritePin(uint32_t Pin, bool PinState) {
HAL_GPIO_WritePin(GPIOA, Pin, PinState ? GPIO_PIN_SET : GPIO_PIN_RESET); // 假设都使用GPIOA
}

bool HAL_GPIO_ReadPin(uint32_t Pin) {
return (HAL_GPIO_ReadPin(GPIOA, Pin) == GPIO_PIN_SET); // 假设都使用GPIOA
}

HAL_pwm.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef HAL_PWM_H
#define HAL_PWM_H

#include <stdint.h>

typedef struct {
uint32_t Channel; // PWM Channel
uint32_t Frequency; // PWM Frequency (Hz)
float DutyCycle; // PWM Duty Cycle (0.0 - 1.0)
} PWM_InitTypeDef;

// 初始化PWM
void HAL_PWM_Init(PWM_InitTypeDef *PWM_InitStruct);

// 设置PWM占空比
void HAL_PWM_SetDutyCycle(uint32_t Channel, float DutyCycle);

#endif // HAL_PWM_H

HAL_pwm.c (示例,基于STM32):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "HAL_pwm.h"
#include "stm32f4xx_hal.h" // 引入STM32 HAL库

void HAL_PWM_Init(PWM_InitTypeDef *PWM_InitStruct) {
TIM_HandleTypeDef htim;
TIM_OC_InitTypeDef sConfigOC;

// 这里需要根据具体的STM32硬件平台选择定时器和通道,并进行时钟使能配置,这里省略
htim.Instance = TIM1; // 假设使用TIM1
htim.Init.Prescaler = SystemCoreClock / PWM_InitStruct->Frequency / 1000 - 1; // 简化计算,假设计数器周期为1000
htim.Init.CounterMode = TIM_COUNTERMODE_UP;
htim.Init.Period = 1000 - 1;
htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim.Init.RepetitionCounter = 0;
HAL_TIM_PWM_Init(&htim);

sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0; // 初始占空比为0
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_LOW;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;

if (PWM_InitStruct->Channel == 1) {
HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_1);
} else if (PWM_InitStruct->Channel == 2) {
HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_2);
} else if (PWM_InitStruct->Channel == 3) {
HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_3);
}
// ... 其他通道配置

HAL_TIM_PWM_Start(&htim, PWM_InitStruct->Channel); // 启动PWM
}

void HAL_PWM_SetDutyCycle(uint32_t Channel, float DutyCycle) {
TIM_HandleTypeDef htim;
htim.Instance = TIM1; // 假设使用TIM1
uint32_t pulse = (uint32_t)(1000 * DutyCycle); // 计算Pulse值
if (Channel == 1) {
__HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_1, pulse);
} else if (Channel == 2) {
__HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_2, pulse);
} else if (Channel == 3) {
__HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_3, pulse);
}
// ... 其他通道设置
}

类似地,我们可以定义 HAL_adc.h, HAL_timer.h, HAL_uart.h 等头文件和对应的.c文件,提供通用的硬件接口。

4.2 驱动层 (Driver Layer)

驱动层负责驱动具体的硬件芯片,例如DRV8302电机驱动芯片、编码器、通信接口等。

drv8302_driver.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#ifndef DRV8302_DRIVER_H
#define DRV8302_DRIVER_H

#include <stdint.h>
#include <stdbool.h>

// DRV8302寄存器地址 (示例,需要查阅DRV8302数据手册)
#define DRV8302_REG_CTRL1 0x00
#define DRV8302_REG_CTRL2 0x01
#define DRV8302_REG_STAT1 0x02
#define DRV8302_REG_STAT2 0x03

// DRV8302初始化结构体
typedef struct {
// ... DRV8302配置参数,例如PWM模式、电流采样增益、保护阈值等
} DRV8302_InitTypeDef;

// 初始化DRV8302
bool DRV8302_Init(DRV8302_InitTypeDef *initStruct);

// 设置DRV8302控制寄存器1
bool DRV8302_SetCtrlReg1(uint16_t regValue);

// 设置DRV8302控制寄存器2
bool DRV8302_SetCtrlReg2(uint16_t regValue);

// 读取DRV8302状态寄存器1
uint16_t DRV8302_GetStatReg1(void);

// 读取DRV8302状态寄存器2
uint16_t DRV8302_GetStatReg2(void);

// 使能电机驱动输出
void DRV8302_EnableDriver(void);

// 禁用电机驱动输出
void DRV8302_DisableDriver(void);

// 检测DRV8302故障状态
bool DRV8302_IsFault(void);

// 清除DRV8302故障状态
void DRV8302_ClearFault(void);

#endif // DRV8302_DRIVER_H

drv8302_driver.c (示例,基于SPI通信):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "drv8302_driver.h"
#include "HAL_spi.h" // 假设使用SPI通信与DRV8302通信
#include "HAL_gpio.h" // 用于控制DRV8302的使能引脚和片选引脚

// DRV8302片选引脚定义 (根据实际硬件连接修改)
#define DRV8302_CS_PIN GPIO_PIN_4

// DRV8302使能引脚定义 (根据实际硬件连接修改)
#define DRV8302_EN_PIN GPIO_PIN_5

// SPI句柄 (需要根据实际硬件平台初始化SPI)
SPI_HandleTypeDef hspi_drv8302;

bool DRV8302_Init(DRV8302_InitTypeDef *initStruct) {
// 初始化DRV8302的片选和使能引脚
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DRV8302_CS_PIN | DRV8302_EN_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(&GPIO_InitStruct);

// 禁用DRV8302驱动输出,并取消片选
HAL_GPIO_WritePin(DRV8302_CS_PIN, true);
HAL_GPIO_WritePin(DRV8302_EN_PIN, false);

// 初始化SPI接口 (需要根据实际硬件平台配置SPI参数)
// ... (SPI初始化代码,例如配置SPI时钟、模式、数据格式等)

// 配置DRV8302寄存器 (根据initStruct参数配置)
// ... (根据DRV8302数据手册,配置控制寄存器1和控制寄存器2)
// 例如:DRV8302_SetCtrlReg1(0x....);
// DRV8302_SetCtrlReg2(0x....);

return true; // 初始化成功
}

// SPI读写函数 (内部函数)
static uint16_t DRV8302_SPI_RW(uint16_t data) {
uint16_t rxData;
HAL_GPIO_WritePin(DRV8302_CS_PIN, false); // 片选使能
HAL_SPI_TransmitReceive(&hspi_drv8302, (uint8_t*)&data, (uint8_t*)&rxData, 1, HAL_MAX_DELAY);
HAL_GPIO_WritePin(DRV8302_CS_PIN, true); // 取消片选
return rxData;
}

bool DRV8302_SetCtrlReg1(uint16_t regValue) {
uint16_t txData = (DRV8302_REG_CTRL1 << 11) | regValue; // 构建发送数据,包含寄存器地址和数据
DRV8302_SPI_RW(txData);
return true;
}

bool DRV8302_SetCtrlReg2(uint16_t regValue) {
uint16_t txData = (DRV8302_REG_CTRL2 << 11) | regValue;
DRV8302_SPI_RW(txData);
return true;
}

uint16_t DRV8302_GetStatReg1(void) {
uint16_t txData = (DRV8302_REG_STAT1 << 11) | (1 << 10); // 读取操作,设置读标志位
return DRV8302_SPI_RW(txData);
}

uint16_t DRV8302_GetStatReg2(void) {
uint16_t txData = (DRV8302_REG_STAT2 << 11) | (1 << 10);
return DRV8302_SPI_RW(txData);
}

void DRV8302_EnableDriver(void) {
HAL_GPIO_WritePin(DRV8302_EN_PIN, true); // 使能DRV8302驱动输出
}

void DRV8302_DisableDriver(void) {
HAL_GPIO_WritePin(DRV8302_EN_PIN, false); // 禁用DRV8302驱动输出
}

bool DRV8302_IsFault(void) {
uint16_t statReg1 = DRV8302_GetStatReg1();
uint16_t statReg2 = DRV8302_GetStatReg2();
// 根据DRV8302状态寄存器位定义,判断是否有故障发生
// 例如:return (statReg1 & (1 << FAULT_BIT)) || (statReg2 & (1 << OTHER_FAULT_BIT));
return false; // 示例,需要根据实际情况解析状态寄存器
}

void DRV8302_ClearFault(void) {
// 清除DRV8302故障状态 (具体操作需要查阅DRV8302数据手册)
// 例如:重新初始化DRV8302或写入特定的控制命令
}

类似地,我们可以编写 encoder_driver.h, encoder_driver.c, comm_driver.h, comm_driver.c 等驱动层代码,分别驱动编码器和通信接口。

4.3 电机控制层 (Motor Control Layer)

电机控制层实现核心的电机控制算法,例如FOC。

foc_controller.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#ifndef FOC_CONTROLLER_H
#define FOC_CONTROLLER_H

#include <stdint.h>

// FOC控制参数结构体
typedef struct {
float kp_id; // Id轴电流环比例系数
float ki_id; // Id轴电流环积分系数
float kp_iq; // Iq轴电流环比例系数
float ki_iq; // Iq轴电流环积分系数
float kp_speed; // 速度环比例系数
float ki_speed; // 速度环积分系数
float target_speed; // 目标速度 (rad/s)
float target_torque; // 目标转矩 (Nm)
} FOC_ControlParamsTypeDef;

// FOC控制器初始化
void FOC_Controller_Init(FOC_ControlParamsTypeDef *params);

// FOC控制循环 (需要周期性调用,例如在定时器中断中)
void FOC_Controller_Loop(float current_ia, float current_ib, float current_ic, float rotor_angle);

// 设置目标速度
void FOC_SetTargetSpeed(float speed);

// 设置目标转矩
void FOC_SetTargetTorque(float torque);

#endif // FOC_CONTROLLER_H

foc_controller.c (示例,简化FOC算法):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include "foc_controller.h"
#include "math.h"
#include "HAL_pwm.h" // 用于设置PWM占空比
#include "HAL_adc.h" // 用于读取电流和电压 (如果需要)

// FOC控制参数
static FOC_ControlParamsTypeDef focParams;

// 电流环PID控制器积分项
static float integral_id = 0;
static float integral_iq = 0;

// 速度环PID控制器积分项
static float integral_speed = 0;

// Clarke变换
static void clarke_transform(float ia, float ib, float *ialpha, float *ibeta) {
*ialpha = ia;
*ibeta = (ia / sqrtf(3.0f)) + (2.0f / sqrtf(3.0f)) * ib;
}

// Park变换
static void park_transform(float ialpha, float ibeta, float angle, float *id, float *iq) {
*id = ialpha * cosf(angle) + ibeta * sinf(angle);
*iq = -ialpha * sinf(angle) + ibeta * cosf(angle);
}

// 反Park变换
static void inv_park_transform(float vd, float vq, float angle, float *valpha, float *vbeta) {
*valpha = vd * cosf(angle) - vq * sinf(angle);
*vbeta = vd * sinf(angle) + vq * cosf(angle);
}

// 空间矢量调制 (SVM) (简化示例,仅为演示目的)
static void svm_modulation(float valpha, float vbeta, float vdc, float *duty_a, float *duty_b, float *duty_c) {
// ... (简化SVM算法实现,实际应用中需要更完整的SVM算法)
// 这里简化为直接将 alpha-beta 电压转换为 PWM 占空比,未考虑扇区判断和矢量合成
*duty_a = 0.5f + valpha / vdc; // 假设 Vdc 为母线电压
*duty_b = 0.5f + vbeta / vdc;
*duty_c = 0.5f - (valpha + vbeta) / vdc; // 假设 ia + ib + ic = 0
// 限制占空比在 0-1 范围内
*duty_a = fmaxf(0.0f, fminf(1.0f, *duty_a));
*duty_b = fmaxf(0.0f, fminf(1.0f, *duty_b));
*duty_c = fmaxf(0.0f, fminf(1.0f, *duty_c));
}

void FOC_Controller_Init(FOC_ControlParamsTypeDef *params) {
focParams = *params;
integral_id = 0;
integral_iq = 0;
integral_speed = 0;
}

void FOC_Controller_Loop(float current_ia, float current_ib, float current_ic, float rotor_angle) {
float ialpha, ibeta, id, iq;
float vd, vq, valpha, vbeta;
float duty_a, duty_b, duty_c;
float speed_error, voltage_d, voltage_q;

// 1. Clarke变换:三相电流 (abc坐标系) -> 两相电流 (alpha-beta坐标系)
clarke_transform(current_ia, current_ib, &ialpha, &ibeta);

// 2. Park变换:两相电流 (alpha-beta坐标系) -> 直轴交轴电流 (dq坐标系)
park_transform(ialpha, ibeta, rotor_angle, &id, &iq);

// 3. 速度环 PI 控制器 (如果需要速度控制)
// 这里简化为电流环控制,如果需要速度环,则需要计算实际速度,并进行速度环PI控制,得到 Iq 轴电流指令
// speed_error = focParams.target_speed - current_speed; // 计算速度误差
// integral_speed += speed_error * focParams.ki_speed; // 积分项累加
// iq_ref = speed_error * focParams.kp_speed + integral_speed; // 速度环输出 Iq 轴电流指令
float iq_ref = focParams.target_torque; // 简化为转矩控制,直接使用目标转矩作为 iq_ref

// 4. 电流环 PI 控制器 (Id轴和Iq轴分别进行PI控制)
voltage_d = (iq - id) * focParams.kp_id + integral_id; // Id轴电流环PI控制
voltage_q = (iq_ref - iq) * focParams.kp_iq + integral_iq; // Iq轴电流环PI控制

integral_id += (iq - id) * focParams.ki_id; // Id轴积分项累加
integral_iq += (iq_ref - iq) * focParams.ki_iq; // Iq轴积分项累加

// 5. 反Park变换:dq轴电压 -> alpha-beta轴电压
inv_park_transform(voltage_d, voltage_q, rotor_angle, &valpha, &vbeta);

// 6. 空间矢量调制 (SVM):alpha-beta轴电压 -> PWM 占空比
// 这里假设母线电压 Vdc 为固定值,实际应用中需要采样母线电压
float vdc = 24.0f; // 假设母线电压为 24V
svm_modulation(valpha, vbeta, vdc, &duty_a, &duty_b, &duty_c);

// 7. 设置PWM占空比,驱动电机
HAL_PWM_SetDutyCycle(1, duty_a); // 假设 PWM 通道 1, 2, 3 分别对应 A, B, C 相
HAL_PWM_SetDutyCycle(2, duty_b);
HAL_PWM_SetDutyCycle(3, duty_c);
}

void FOC_SetTargetSpeed(float speed) {
focParams.target_speed = speed;
}

void FOC_SetTargetTorque(float torque) {
focParams.target_torque = torque;
}

4.4 应用层 (Application Layer)

应用层负责处理用户指令、系统状态管理、通信协议处理等。

main.c (示例,简化主程序):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "main.h"
#include "HAL_gpio.h"
#include "HAL_pwm.h"
#include "drv8302_driver.h"
#include "encoder_driver.h" // 假设有编码器驱动
#include "foc_controller.h"
#include "stdio.h" // 用于printf调试

// 定义GPIO引脚 (根据实际硬件连接修改)
#define LED_PIN GPIO_PIN_13

int main(void) {
// 1. 初始化HAL层
HAL_Init(); // 初始化 STM32 HAL 库
SystemClock_Config(); // 配置系统时钟

// 初始化LED GPIO
GPIO_InitTypeDef LED_GPIO_InitStruct = {0};
LED_GPIO_InitStruct.Pin = LED_PIN;
LED_GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
LED_GPIO_InitStruct.Pull = GPIO_PULLDOWN;
LED_GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
HAL_GPIO_Init(&LED_GPIO_InitStruct);

// 初始化PWM
PWM_InitTypeDef PWM_InitStruct = {0};
PWM_InitStruct.Channel = 1; // 通道1
PWM_InitStruct.Frequency = 20000; // 20kHz PWM频率
PWM_InitStruct.DutyCycle = 0.0f; // 初始占空比为0
HAL_PWM_Init(&PWM_InitStruct);
PWM_InitStruct.Channel = 2; // 通道2
HAL_PWM_Init(&PWM_InitStruct);
PWM_InitStruct.Channel = 3; // 通道3
HAL_PWM_Init(&PWM_InitStruct);

// 初始化DRV8302
DRV8302_InitTypeDef drv8302InitStruct;
// ... (配置DRV8302初始化参数,例如PWM模式、电流采样增益等)
DRV8302_Init(&drv8302InitStruct);
DRV8302_EnableDriver(); // 使能电机驱动输出

// 初始化编码器
Encoder_Init(); // 假设有 Encoder_Init() 函数

// 初始化FOC控制器
FOC_ControlParamsTypeDef focControlParams;
focControlParams.kp_id = 1.0f; // 电流环 PI 参数
focControlParams.ki_id = 0.1f;
focControlParams.kp_iq = 1.0f;
focControlParams.ki_iq = 0.1f;
focControlParams.kp_speed = 0.1f; // 速度环 PI 参数 (如果需要速度控制)
focControlParams.ki_speed = 0.01f;
focControlParams.target_speed = 0.0f; // 初始目标速度为 0
focControlParams.target_torque = 0.1f; // 初始目标转矩
FOC_Controller_Init(&focControlParams);

// 主循环
while (1) {
HAL_GPIO_TogglePin(LED_PIN); // LED 闪烁,指示程序运行

// 读取编码器角度 (假设 Encoder_GetAngle() 返回转子角度,单位为弧度)
float rotor_angle = Encoder_GetAngle();

// 读取电机电流 (假设有电流传感器和 ADC 采样,这里简化为固定值)
float current_ia = 0.1f;
float current_ib = -0.05f;
float current_ic = -0.05f;

// FOC 控制循环
FOC_Controller_Loop(current_ia, current_ib, current_ic, rotor_angle);

HAL_Delay(10); // 控制循环周期,例如 10ms
}
}

// 系统时钟配置函数 (示例,需要根据实际硬件平台配置时钟)
void SystemClock_Config(void) {
// ... (STM32 系统时钟配置代码,例如配置 HSE, HSI, PLL 等)
}

// HAL库初始化错误处理函数 (示例)
void Error_Handler(void) {
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1) {
printf("HAL Error!\r\n"); // 通过串口输出错误信息
}
}

5. 测试验证

在代码开发完成后,我们需要进行全面的测试验证,以确保系统的功能、性能和可靠性满足需求。测试验证主要包括以下几个方面:

  • 单元测试: 对每个模块进行独立测试,例如HAL层、驱动层、电机控制层等。可以使用单元测试框架 (例如 CMocka, Unity) 编写测试用例,验证模块的功能是否正确。
  • 集成测试: 将各个模块组合起来进行测试,例如测试电机控制层与驱动层的集成,驱动层与HAL层的集成等。验证模块之间的接口是否正确,数据传递是否正常。
  • 系统测试: 对整个系统进行测试,验证系统的整体功能和性能是否满足需求。例如,测试电机是否能够按照指令精确运动,速度和转矩控制是否准确,保护功能是否有效等。
  • 性能测试: 测试系统的性能指标,例如电机控制的带宽、响应速度、效率等。可以使用示波器、功率分析仪等工具进行测量。
  • 可靠性测试: 进行长时间运行测试、环境测试 (例如高温、低温、振动等),验证系统的可靠性和稳定性。
  • 安全性测试: 测试系统的保护功能是否有效,例如过流保护、过压保护、过温保护等。

6. 维护升级

为了方便后续的维护和升级,我们需要考虑以下几个方面:

  • 固件升级: 系统需要支持固件升级功能,方便bug修复和功能更新。可以使用USB DFU (Device Firmware Upgrade) 或其他OTA (Over-The-Air) 升级方式。
  • 日志记录: 在代码中添加日志记录功能,方便调试和故障排查。可以使用串口、SD卡或其他存储介质记录日志信息。
  • 参数配置: 将一些重要的参数 (例如 PID 参数、电机参数、保护阈值等) 放在可配置的位置,方便用户根据实际情况进行调整。可以使用配置文件、上位机配置工具等方式进行参数配置。
  • 代码版本管理: 使用版本控制系统 (例如 Git) 管理代码,方便代码的版本管理和协作开发。
  • 文档编写: 编写详细的开发文档、用户手册等,方便用户理解和使用系统。

总结

以上是我为你提供的自制ODrive 3.5V,24V/30A项目的代码设计架构和C代码实现方案。这个方案采用了分层架构设计模式,将系统划分为应用层、电机控制层、驱动层、HAL层和底层硬件层,提高了代码的可维护性、可扩展性和可移植性。代码示例涵盖了HAL层、驱动层、电机控制层和应用层的一些核心模块,展示了系统架构的具体实现思路。

需要注意的是,以上代码示例仅为演示目的,实际项目开发中还需要根据具体的硬件平台、电机型号、应用场景等进行详细的设计和实现。同时,完整的嵌入式系统开发还需要进行深入的需求分析、硬件设计、详细的代码编写、全面的测试验证和持续的维护升级。

希望这个详细的解答能够帮助你更好地理解嵌入式系统开发,并为你的自制ODrive 3.5V项目提供有价值的参考。如果你有任何进一步的问题,欢迎随时提出。

欢迎关注我的其它发布渠道