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 初始化阶段

  1. 初始化函数

    initAfterJump();
    initCorePeripherals();
    enableCorePeripherals();
    loadEEpromSettings();
    

    这些函数用于初始化系统外围设备并从EEPROM加载设置。

  2. EEPROM地址设置

    if((*(uint32_t*)(0x08000FE0)) == 0xf8){
        eeprom_address = (uint32_t)0x0800F800;
    }
    

    这段代码检查特定内存地址,并在满足条件时设置EEPROM地址。

4.2 固件版本处理

  1. 固件版本检查

    #ifdef USE_MAKE
    // 固件版本比较和更新
    #else
    // 备用版本检查和更新
    #endif
    

    这部分代码检查当前固件版本是否与存储版本匹配,并在必要时进行更新。

4.3 电机控制设置

  1. 电机控制变量

    if (use_sin_start) { }
    if (dir_reversed == 1) { forward = 0; } else { forward = 1; }
    

    根据配置设置电机方向和启动模式。

  2. 互补PWM检查

    if (!comp_pwm) { use_sin_start = 0; }
    

    确保正弦启动模式需要互补PWM。

  3. RC汽车反向模式

    if (RC_CAR_REVERSE) {
        // 设置反向模式的多个参数
    }
    

    如果启用了RC汽车反向模式,将覆盖多个参数。

4.4 MCU特定设置

  1. MCU特定初始化

    #ifdef MCU_F031
    // 特定MCU的GPIO和其他初始化
    #endif
    

    处理特定于所使用MCU的初始化。

4.5 主循环

  1. 看门狗和输入处理

    while (1) {
        RELOAD_WATCHDOG_COUNTER();
        // 处理输入和电机控制
    }
    

    主循环重新加载看门狗计数器以防止系统复位,并处理电机控制的输入。

4.6 遥测和安全检查

  1. 遥测处理

    if (send_telemetry) {
        // 发送遥测数据
    }
    

    在需要时发送遥测数据。

  2. ADC和温度监测

    if (adc_counter > 200) {
        // ADC回调和温度计算
    }
    

    定期检查电池电压、电流和温度的ADC值。

4.7 电机换向和控制

  1. 电机换向计时

    e_com_time = // 计算换向时间
    

    计算电机换向的时间间隔。

  2. 可变PWM调整

    if (VARIABLE_PWM) {
        // 根据换向间隔调整PWM频率
    }
    

    调整PWM频率以控制电机。

  3. 失步检查

    if (desync_check && zero_crosses > 10) {
        // 处理失步
    }
    

    检查并处理电机失步事件。

4.8 附加功能

  1. 步进/正弦模式处理

    if (stepper_sine == 0) {
        // 常规电机模式
    } else {
        // 步进或正弦模式
    }
    

    处理电机的不同操作模式,包括步进或正弦波控制。

  2. 有刷模式处理

    #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代码库

AM32开源代码之代码分析 - EEprom

  • map
  • getAbsDif
  • delayMicros
  • delayMillis

AM32开源代码之代码分析 - function

  • computeDshotDMA
  • processDshot
  • transfercomplete
  • make_dshot_package

AM32开源代码之代码分析 - DSHOT/BDSHOT

  • getBemfState
  • commutate
  • setInput
  • advanceincrement
  • zcfoundroutine
  • setPWMCompare1

AM32开源代码之代码分析 - 正选控制

  • void WS2812_Init(void)
  • void send_LED_RGB(uint8_t red, uint8_t green, uint8_t blue)
  • void send_LED_DMA()

AM32开源代码之代码分析 - WS2812/RGB LED

  • ADC_DMA_Callback
  • LL_ADC_REG_StartConversion
  • __LL_ADC_CALC_TEMPERATURE
  • getConvertedDegrees
  • getSmoothedCurrent
  • adc_software_trigger_enable
  • adc_ordinary_software_trigger_enable

AM32开源代码之代码分析 - ADC

  • send_telem_DMA
  • makeTelemPackage

AM32开源代码之代码分析 - Telemetry

// 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 无刷直流电子调速器驱动方式

  • 40
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值