
AM32开源代码之工程结构
通过 Betaflight 通透、单线串行或 Arduino 升级固件支持 Servo PWM 和 Dshot(300, 600)电机协议支持双向 DshotKISS 标准 ESC 遥测可变 PWM 频率正弦启动模式,专为使大型电机加速而设计Windows离线工具Linux理想工具Web在线工具最新代码下载AM32开源代码。
AM32开源代码之工程结构
1. 源由
从程序员的角度:C代码,首先看工程结构,然后看文件命名,其次看代码格式。
因为,这个流程看下来,心里基本上对于该程序的成熟度,代码质量在感性上有了初步的猜测。
所以,这里先从这里研读下代码,并整理一个大体的感受!
2. AM32简介
AM32主要特点:
- 通过 Betaflight 通透、单线串行或 Arduino 升级固件
- 支持 Servo PWM 和 Dshot(300, 600)电机协议
- 支持双向 Dshot
- KISS 标准 ESC 遥测
- 可变 PWM 频率
- 正弦启动模式,专为使大型电机加速而设计
AM32配置工具:
AM32代码相关:
3. AM32目录结构
├──> Inc //公共模块头文件
│ ├──> common.h
│ ├──> dshot.h
│ ├──> firmwareversion.h
│ ├──> functions.h
│ ├──> signal.h
│ ├──> sounds.h
│ ├──> targets.h
│ └──> version.h
├──> Src //公共模块源文件
│ ├──> dshot.c
│ ├──> firmwareversion.c
│ ├──> functions.c
│ ├──> main.c
│ ├──> signal.c
│ └──> sounds.c
├──> Mcu //BSP底层代码
│ ├──> e230
│ ├──> f031
│ ├──> f051
│ ├──> f415
│ ├──> f421
│ ├──> g071
│ ├──> g431
│ └──> l431
├──> Keil_Projects //Keil工程配置文件
├──> doc/development //文档资料(含Linux下arm-gcc编译器,命令行编译指南)
├──> make
└──> tools
4. AM32主程序框架
该main
主函数包含了一个ESC的全面设置和控制循环,管理初始化、固件更新、电机控制、遥测和安全检查。代码通过条件编译结构支持各种配置和MCU类型,确保在不同应用中的通用性。
4.1 初始化阶段
-
初始化函数:
initAfterJump(); initCorePeripherals(); enableCorePeripherals(); loadEEpromSettings();
这些函数用于初始化系统外围设备并从EEPROM加载设置。
-
EEPROM地址设置:
if((*(uint32_t*)(0x08000FE0)) == 0xf8){ eeprom_address = (uint32_t)0x0800F800; }
这段代码检查特定内存地址,并在满足条件时设置EEPROM地址。
4.2 固件版本处理
-
固件版本检查:
#ifdef USE_MAKE // 固件版本比较和更新 #else // 备用版本检查和更新 #endif
这部分代码检查当前固件版本是否与存储版本匹配,并在必要时进行更新。
4.3 电机控制设置
-
电机控制变量:
if (use_sin_start) { } if (dir_reversed == 1) { forward = 0; } else { forward = 1; }
根据配置设置电机方向和启动模式。
-
互补PWM检查:
if (!comp_pwm) { use_sin_start = 0; }
确保正弦启动模式需要互补PWM。
-
RC汽车反向模式:
if (RC_CAR_REVERSE) { // 设置反向模式的多个参数 }
如果启用了RC汽车反向模式,将覆盖多个参数。
4.4 MCU特定设置
-
MCU特定初始化:
#ifdef MCU_F031 // 特定MCU的GPIO和其他初始化 #endif
处理特定于所使用MCU的初始化。
4.5 主循环
-
看门狗和输入处理:
while (1) { RELOAD_WATCHDOG_COUNTER(); // 处理输入和电机控制 }
主循环重新加载看门狗计数器以防止系统复位,并处理电机控制的输入。
4.6 遥测和安全检查
-
遥测处理:
if (send_telemetry) { // 发送遥测数据 }
在需要时发送遥测数据。
-
ADC和温度监测:
if (adc_counter > 200) { // ADC回调和温度计算 }
定期检查电池电压、电流和温度的ADC值。
4.7 电机换向和控制
-
电机换向计时:
e_com_time = // 计算换向时间
计算电机换向的时间间隔。
-
可变PWM调整:
if (VARIABLE_PWM) { // 根据换向间隔调整PWM频率 }
调整PWM频率以控制电机。
-
失步检查:
if (desync_check && zero_crosses > 10) { // 处理失步 }
检查并处理电机失步事件。
4.8 附加功能
-
步进/正弦模式处理:
if (stepper_sine == 0) { // 常规电机模式 } else { // 步进或正弦模式 }
处理电机的不同操作模式,包括步进或正弦波控制。
-
有刷模式处理:
#ifdef BRUSHED_MODE runBrushedLoop(); #endif
包含特定于有刷电机的循环(如果已配置)。
5. 主程序框架 - 注释
int main(void)
{
// 初始化跳转后的设置
initAfterJump();
// 初始化核心外设
initCorePeripherals();
// 启用核心外设
enableCorePeripherals();
// 加载 EEPROM 设置
loadEEpromSettings();
// 根据 EEPROM 数据设置 EEPROM 地址
if((*(uint32_t*)(0x08000FE0)) == 0xf8){
eeprom_address = (uint32_t)0x0800F800;
}
#ifdef USE_MAKE
// 如果固件版本不匹配,则更新 EEPROM 设置
if (firmware_info.version_major != eepromBuffer[3] || firmware_info.version_minor != eepromBuffer[4]) {
eepromBuffer[3] = firmware_info.version_major;
eepromBuffer[4] = firmware_info.version_minor;
for (int i = 0; i < 12; i++) {
eepromBuffer[5 + i] = firmware_info.device_name[i];
}
saveEEpromSettings();
}
#else
// 如果固件版本不匹配,则更新 EEPROM 设置
if (VERSION_MAJOR != eepromBuffer[3] || VERSION_MINOR != eepromBuffer[4]) {
eepromBuffer[3] = VERSION_MAJOR;
eepromBuffer[4] = VERSION_MINOR;
for (int i = 0; i < 12; i++) {
eepromBuffer[5 + i] = (uint8_t)FIRMWARE_NAME[i];
}
saveEEpromSettings();
}
#endif
// 根据使用情况设置
if (use_sin_start) {
// min_startup_duty = sin_mode_min_s_d; // 未使用
}
if (dir_reversed == 1) {
forward = 0;
} else {
forward = 1;
}
tim1_arr = TIMER1_MAX_ARR;
// 调整 PWM 频率的相关设置(已注释)
// startup_max_duty_cycle = startup_max_duty_cycle * TIMER1_MAX_ARR / 2000 + dead_time_override;
// throttle_max_at_low_rpm = throttle_max_at_low_rpm * TIMER1_MAX_ARR / 2000;
// throttle_max_at_high_rpm = TIMER1_MAX_ARR;
// 如果不使用互补 PWM,则禁用正弦启动
if (!comp_pwm) {
use_sin_start = 0;
}
// 如果启用了 RC 车反向模式,则覆盖多项设置
if (RC_CAR_REVERSE) {
throttle_max_at_low_rpm = 1000;
bi_direction = 1;
use_sin_start = 0;
low_rpm_throttle_limit = 1;
VARIABLE_PWM = 0;
// stall_protection = 1; // 未使用
comp_pwm = 0;
stuck_rotor_protection = 0;
minimum_duty_cycle = minimum_duty_cycle + 50;
stall_protect_minimum_duty = stall_protect_minimum_duty + 50;
min_startup_duty = min_startup_duty + 50;
}
#ifdef MCU_F031
// 解除 MCU_F031 的待机模式,并设置输出电平
GPIOF->BSRR = LL_GPIO_PIN_6;
GPIOF->BRR = LL_GPIO_PIN_7;
GPIOA->BRR = LL_GPIO_PIN_11;
#endif
#ifdef USE_LED_STRIP
// 设置 LED 条带颜色
send_LED_RGB(125, 0, 0);
#endif
#ifdef USE_CRSF_INPUT
inputSet = 1;
playStartupTune();
MX_IWDG_Init();
LL_IWDG_ReloadCounter(IWDG);
#else
#if defined(FIXED_DUTY_MODE) || defined(FIXED_SPEED_MODE)
MX_IWDG_Init();
RELOAD_WATCHDOG_COUNTER();
inputSet = 1;
armed = 1;
adjusted_input = 48;
newinput = 48;
comStep(2);
#ifdef FIXED_SPEED_MODE
use_speed_control_loop = 1;
use_sin_start = 0;
target_e_com_time = 60000000 / FIXED_SPEED_MODE_RPM / (motor_poles / 2);
input = 48;
#endif
#else
#ifdef BRUSHED_MODE
// 处理刷式电机模式的初始化
// bi_direction = 1; // 已注释
commutation_interval = 5000;
use_sin_start = 0;
maskPhaseInterrupts();
playBrushedStartupTune();
#else
#ifdef MCU_AT415
play_tone_flag = 5;
#else
playStartupTune();
#endif
#endif
zero_input_count = 0;
MX_IWDG_Init();
RELOAD_WATCHDOG_COUNTER();
#ifdef GIMBAL_MODE
bi_direction = 1;
use_sin_start = 1;
#endif
#ifdef USE_ADC_INPUT
armed_count_threshold = 5000;
inputSet = 1;
#else
// 检查信号线的状态
// checkForHighSignal(); // 将在信号线高电平时重启
receiveDshotDma();
if (drive_by_rpm) {
use_speed_control_loop = 1;
}
#endif
#endif // end fixed duty mode ifdef
#endif // end crsf input
#ifdef MCU_F051
// 获取 MCU ID 和版本号
MCU_Id = DBGMCU->IDCODE &= 0xFFF;
REV_Id = DBGMCU->IDCODE >> 16;
// 根据版本号设置温度偏移
if (REV_Id >= 4096) {
temperature_offset = 0;
} else {
temperature_offset = 230;
}
#endif
#ifdef NEUTRONRC_G071
setInputPullDown();
#else
setInputPullUp();
#endif
#ifdef USE_INVERTED_HIGH
// 调整启动和最小占空比
min_startup_duty = min_startup_duty + 100;
minimum_duty_cycle = minimum_duty_cycle + 100;
#endif
while (1) {
#ifdef FIXED_DUTY_MODE
setInput();
#endif
#ifdef MCU_F031
if (input_ready) {
processDshot();
input_ready = 0;
}
#endif
RELOAD_WATCHDOG_COUNTER();
e_com_time = ((commutation_intervals[0] + commutation_intervals[1] + commutation_intervals[2] + commutation_intervals[3] + commutation_intervals[4] + commutation_intervals[5]) + 4) >> 1; // 电气转速时间
if (VARIABLE_PWM) {
tim1_arr = map(commutation_interval, 96, 200, TIMER1_MAX_ARR / 2, TIMER1_MAX_ARR);
}
if (signaltimeout > (LOOP_FREQUENCY_HZ >> 1)) { // 半秒超时
if (armed) {
allOff();
armed = 0;
input = 0;
inputSet = 0;
zero_input_count = 0;
SET_DUTY_CYCLE_ALL(0);
resetInputCaptureTimer();
for (int i = 0; i < 64; i++) {
dma_buffer[i] = 0;
}
NVIC_SystemReset();
}
if (signaltimeout > LOOP_FREQUENCY_HZ << 1) { // 2秒超时
allOff();
armed = 0;
input = 0;
inputSet = 0;
zero_input_count = 0;
SET_DUTY_CYCLE_ALL(0);
resetInputCaptureTimer();
for (int i = 0; i < 64; i++) {
dma_buffer[i] = 0;
}
NVIC_SystemReset();
}
}
#ifdef USE_CUSTOM_LED
// 控制自定义 LED 的状态
if ((input >= 47) && (input < 1947)) {
if (ledcounter > (2000 >> forward)) {
GPIOB->BSRR = LL_GPIO_PIN_3;
} else {
GPIOB->BRR = LL_GPIO_PIN_3;
}
if (ledcounter > (4000 >> forward)) {
ledcounter = 0;
}
}
if (input > 1947) {
GPIOB->BSRR = LL_GPIO_PIN_3;
}
if (input < 47) {
GPIOB->BRR = LL_GPIO_PIN_3;
}
#endif
if (tenkhzcounter > LOOP_FREQUENCY_HZ) { // 1秒采样间隔
consumed_current = (float)actual_current / 360 + consumed_current;
switch (dshot_extended_telemetry) {
case 1:
send_extended_dshot = 0b0010 << 8 | degrees_celsius;
dshot_extended_telemetry = 2;
break;
case 2:
send_extended_dshot = 0b0110 << 8 | (uint8_t)actual_current / 50;
dshot_extended_telemetry = 3;
break;
case 3:
send_extended_dshot = 0b1110 << 8 | ((uint8_t)consumed_current / 50);
dshot_extended_telemetry = 1;
break;
default:
send_extended_dshot = 0b0010 << 8 | degrees_celsius;
dshot_extended_telemetry = 1;
break;
}
tenkhzcounter = 0;
}
if (input == 0) {
if (bi_direction) {
if (zero_input_count < 20) {
zero_input_count++;
}
} else {
zero_input_count = 0;
}
} else {
zero_input_count = 0;
}
if (use_speed_control_loop) {
speed_loop_control();
} else {
duty_loop_control();
}
}
return 0;
}
6. 代码分析
// 这份代码目前尚不完善,之前Alka开源的代码位置比较分散,本工程可能希望整合起来,所以尚不完整,比如:bootloader截止写稿日尚未引入该AM32代码库
-
loadEEpromSettings
-
saveEEpromSettings
- map
- getAbsDif
- delayMicros
- delayMillis
- computeDshotDMA
- processDshot
- transfercomplete
- make_dshot_package
- getBemfState
- commutate
- setInput
- advanceincrement
- zcfoundroutine
- setPWMCompare1
- void WS2812_Init(void)
- void send_LED_RGB(uint8_t red, uint8_t green, uint8_t blue)
- void send_LED_DMA()
- ADC_DMA_Callback
- LL_ADC_REG_StartConversion
- __LL_ADC_CALC_TEMPERATURE
- getConvertedDegrees
- getSmoothedCurrent
- adc_software_trigger_enable
- adc_ordinary_software_trigger_enable
- send_telem_DMA
- makeTelemPackage
// TODO: 代码分类及完善整理后续更新,请关注。。。。
- initAfterJump
- initCorePeripherals
- enableCorePeripherals
- MX_IWDG_Init
- LL_IWDG_ReloadCounter
- RELOAD_WATCHDOG_COUNTER
- comStep
- maskPhaseInterrupts
- receiveDshotDma
- setInputPullDown
- setInputPullUp
- allOff
- SET_DUTY_CYCLE_ALL
- resetInputCaptureTimer
- NVIC_SystemReset
- NVIC_SetPriority
- allpwm
- DISABLE_COM_TIMER_INT
- SET_INTERVAL_TIMER_COUNT
- generatePwmTimerEvent
- proportionalBrake
- playStartupTune
- playBrushedStartupTune
- runBrushedLoop
7. 参考资料
【1】BLDC ESC 无刷直流电子调速器简介
【2】BLDC ESC 无刷直流电子调速器工作原理
【3】BLDC ESC 无刷直流电子调速器工作过程
【4】BLDC ESC 无刷直流电子调速器驱动方式

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)