YuanHub 源码分析【四】位置速度

情系淮思:五款车规MCU横向对比与VCU选型 本文对比五款车规MCU中的S32K312与S32K324,聚焦核心架构、存储、通信及安全特性。S32K324为双核160MHz Cortex-M7,内存、外设(如以太网、QuadSPI、SAI)全面升级,支持高实时并行处理与复杂应用;而S32K312为单核120MHz,适用于中端车身控制。两者均满足ASIL B功能安全,具备完整安全引擎。选型建议:中端用S32K312,高端复杂系统优选S32K324。 阅读详情

一、定时器外设初始化和中断

1.1 初始化

外设由 CubeMX 初始化:

根据宏定义我们可知,位置环是在 TIM5 上初始化:

#define POSITION_LOOP_TIM_HANDLE    (htim5)                     //位置环定时器
#define POSITION_LOOP_TIM_IRQ_TASK  TIM5_IRQHandler             //位置环定时器中断回调函数

下面是 TIM5 的初始化代码:

int main(void)
{
  ...
  MX_TIM5_Init();
  ...
}
/**
  * @brief TIM5 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM5_Init(void)
{

  /* USER CODE BEGIN TIM5_Init 0 */

  /* USER CODE END TIM5_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM5_Init 1 */

  /* USER CODE END TIM5_Init 1 */
  htim5.Instance = TIM5;
  htim5.Init.Prescaler = 0;
  htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim5.Init.Period = 24000-1;
  htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim5) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM5_Init 2 */

  /* USER CODE END TIM5_Init 2 */

}
分类参数项目配置值功能说明

计数器

计数模式 (CounterMode)向上计数 (Up)计数器从 0 递增到周期值(24000-1),然后溢出并重新从 0 开始

周期 (Period)24000-1决定定时器的溢出周期/基础频率,即自动重装载寄存器 (ARR) 的值

预分频 (Prescaler)0定时器时钟不分频(实际分频系数为 0+1=1),保持总线输入的最高时钟频率

时钟分频 (ClockDivision)不分频 (DIV1)决定数字滤波器和死区时间生成的采样时钟频率,此处与定时器时钟一致

自动重载预装载

 

(AutoReloadPreload)

使能 (Enable)开启缓冲机制,运行时修改周期值会在下一个更新事件才生效,防止波形突变(毛刺)

时钟源

时钟源选择 (ClockSource)内部时钟 (Internal)使用微控制器内部的总线时钟(通常是 APB1)作为定时器驱动源

触发同步

主模式触发输出 (TRGO)复位 (Reset)定时器的复位信号(UG位产生)用作主输出触发,可用于触发级联的从定时器或其他外设

主从模式 (MasterSlave)禁用 (Disable)禁用主从同步模式,定时器作为独立模块正常运行

二、中断函数

2.1 位置环中断函数

POSITION_LOOP_TIM_IRQ_TASK()

// 位置环中断任务 典型频率  10KHZ
void POSITION_LOOP_TIM_IRQ_TASK(void)
{
    POSITION_LOOP_TIM_HANDLE.Instance->SR &= ~TIM_SR_UIF;

    bsp_set_timer_record_stop(SYS_TIMER_RECORD_POSITION_LOOP_CYCLE_INDEX); // 测量位置环周期
    bsp_set_timer_record_start(SYS_TIMER_RECORD_POSITION_LOOP_CYCLE_INDEX);

    bsp_set_timer_record_start(SYS_TIMER_RECORD_POSITION_LOOP_TIME_INDEX); // 测量位置环运行时间

#ifndef VIRTUAL_MOTOR_MODEL
    if (sys_get_hardware_self_test_status() == false)
    {
        EncoderDataInit();
        HardwareSelfTestRun();
    }

    // 编码器数据读取及处理
    EncoderDataRead();
    EncoderDataProcess();

    bsp_pwm_ready_state_updata(); // 更新PWM输出准备状态
#endif // VIRTUAL_MOTOR_MODEL
    if (sys_get_hardware_self_test_status() == true)
    {
        PosSpeedLoopCtrl();
    }
    bsp_set_timer_record_stop(SYS_TIMER_RECORD_POSITION_LOOP_TIME_INDEX);
}

2.2 计时器

其使用了 DWT 时钟周期计数器来记录时间,共有:

  • sys_timer_init()
  • set_timer_record_start()
  • set_timer_record_stop()
  • get_timer_duration_records_us()

四个函数。

下面是 CURRENT_LOOP_IRQ_TASK() 所使用的计时器代码:

SysTimer_t sys_timer = {
    .sys_freq_Mhz = 0,
    .sys_freq_Mhz_inv = 0,
    .now_records = {0},
    .duration_records = {0},
};
 
void sys_timer_init(void)
{
    if (!((DEM_CR & DEM_CR_TRCENA) && DWT_CR & DWT_CR_CYCCNTENA))
    {
        DEM_CR |= DEM_CR_TRCENA;//第一步使能DWT外设
        DWT_LAR |= DWT_LAR_UNLOCK;
        DWT_CYCCNT = 0u;//第二步在使能DWT计数前先清零
        DWT_CR |= DWT_CR_CYCCNTENA;//第三步使能DWT计数
    }
    sys_timer.sys_freq_Mhz = HAL_RCC_GetSysClockFreq() / 1000000;
    sys_timer.sys_freq_Mhz_inv = 1.0f / (float)sys_timer.sys_freq_Mhz;
}
 
void set_timer_record_start(SYS_TIMER_RECORD_INDEX index)
{
    sys_timer.now_records[index] = DWT_CYCCNT;
}
 
void set_timer_record_stop(SYS_TIMER_RECORD_INDEX index)
{
    sys_timer.duration_records[index] = DWT_CYCCNT - sys_timer.now_records[index];
}
 
float get_timer_duration_records_us(SYS_TIMER_RECORD_INDEX index)
{
    return sys_timer.sys_freq_Mhz_inv * sys_timer.duration_records[index];
}

2.2 编码器初始化

这里包括了编码器初始化的内容:

    if (sys_get_hardware_self_test_status() == false)
    {
        EncoderDataInit();
        HardwareSelfTestRun();
    }

    // 编码器数据读取及处理
    EncoderDataRead();
    EncoderDataProcess();

    bsp_pwm_ready_state_updata(); // 更新PWM输出准备状态

2.3 更新PWM输出准备状态函数

在实际的电机驱动硬件中,当我们用软件下发 使能 PWM 的指令时,底层的门级驱动芯片 (Gate Driver) 内部的自举电容充电、电荷泵稳压、内部逻辑复位等硬件动作都需要一定的时间,通常是几个到几十个毫秒。如果在芯片刚上电还没稳妥的时候,单片机就立刻往里面塞高频的 PWM 波形,很容易导致硬件报错甚至炸管。

bsp_pwm_ready_state_updata() 函数的功能是在使能 PWM 的时候给驱动器的自举电容充电,其充电时间由计数器宏定义 DRIVER_PWM_READY_TIME 决定。

代码如下:

/**
 * @brief 更新PWM输出准备状态
 */
void bsp_pwm_ready_state_updata(void)
{
#ifdef VIRTUAL_MOTOR_MODEL
    kBspData.pwm_ready_state = true;
#else
    if (DRIVER_PWM_READY_TIME)
    {
        if (kBspData.pwm_en_state == PWM_ENABLE)
        {
            kBspData.pwm_state_cnt++;
            if (kBspData.pwm_state_cnt >= DRIVER_PWM_READY_TIME)
            {
                kBspData.pwm_state_cnt = DRIVER_PWM_READY_TIME;
                kBspData.pwm_ready_state = true;
            }
        }
        else
        {
            kBspData.pwm_state_cnt = 0;
            kBspData.pwm_ready_state = false;
        }
    }
    else // 等待时间为0,PWM使能后立即认为PWM准备好
    {
        kBspData.pwm_ready_state = true;
    }
#endif
}

三、速度环中断

3.1 逻辑总览

3.2 位置速度环中断函数

PosSpeedLoopCtrl() 函数是位置环和速度环的控制总调度。

除其一些逻辑处理外,主要包括了 AppRun()、 ModeRun()、CurrentCtlLoopTask() 三个函数。


/**
 * @brief 位置速度环中断循环
 */
void PosSpeedLoopCtrl(void)
{
    // 位置速度环系统输入
    // 1. 读取当前实际位置  编码器当前单圈位置
    kAxis.motor_pos_sensor_input.enc_counts_now_p = bsp_get_encoder_cnt(ENCODER_ID_MOTOR);
    kAxis.load_pos_sensor_input.enc_counts_now_p = bsp_get_encoder_cnt(ENCODER_ID_LOAD);

    if (get_app_Internal_control_authority() == INTERNAL_CONTROL_APP)
    {
        AppStatusCheck();
        AppStatusScanFast();
        AppMotionPreConditionRun();
#ifdef USE_CIA402
        Cia402ErrorCodeScan();
        PDS_FSA_Run();
#endif // USE_CIA402
        AppRun(kAxis.motor_ctl_sm_input.hw_ready_state);
#ifdef USE_CIA402
        UpdateStatusword();
#endif // USE_CIA402
    }

    // 2. 执行位置速度环控制算法
    PosSpeedCtlLoopTask(&kAxis, &kAxisDw);

    // 3. 输出电机控制电压  失能状态到使能状态 上升沿使能电机
    if (kAxis.motor_ctl_sm_output.state == MOTOR_CTL_SM_STATE_ENABLE)
    {
        if (sys_get_current_calibration_status() == CURRENT_CALIBRATION_STATUS_OK)
        {
            if (kAxis.motor_ctl_sm_output.state_last == MOTOR_CTL_SM_STATE_DISABLE)
            {
                bsp_set_pwm_state(PWM_ENABLE);
            }
        }
        else
        {
            sys_current_calibration_step();
        }
    }
    else // 失能状态    其他状态都失能电机  包括错误状态
    {
        bsp_set_pwm_state(PWM_DISABLE);
    }

    // 4. 更新硬件准备状态
    if (bsp_get_pwm_state() == true &&\
        sys_get_current_calibration_status() == CURRENT_CALIBRATION_STATUS_OK)
    {
        kAxis.motor_ctl_sm_input.hw_ready_state = 1;
    }
    else
    {
        kAxis.motor_ctl_sm_input.hw_ready_state = 0;
    }
    // 记录上一次控制层状态机状态
    kAxis.motor_ctl_sm_output.state_last = kAxis.motor_ctl_sm_output.state;


    // 同步控制层变量到应用层,实现波形动态刷新和离线波形采集
    AppParamSyncUser();

    // 运行离线波形采样
    ScopeIsrHandle();
}

3.2 AppRun() 系列逻辑

3.2.1 AppRun()

在 AppRun() 中选择合适的启动模式程序,通过函数指针方式调用,所有模式的初始化、运行、启动、停止函数等均在这个表格内:

// 应用程序列表
static const AppTable app_table[APP_NUM] =
{
    {APP_PP_MODE, PpModeInit, PpModeStart, PpModeRun, PpModeStop, APP_TYPE_PDS},
    {APP_PV_MODE, PvModeInit, PvModeStart, PvModeRun, PvModeStop, APP_TYPE_PDS},
    {APP_PT_MODE, PtModeInit, PtModeStart, PtModeRun, PtModeStop, APP_TYPE_PDS},
    {APP_MIT_MODE, MitModeInit, MitModeStart, MitModeRun, MitModeStop, APP_TYPE_PDS},
    {APP_HM_MODE, HomingModeInit, HomingModeStart, HomingModeRun, HomingModeStop, APP_TYPE_PDS},
    {APP_CSP_MODE, CspModeInit, CspModeStart, CspModeRun, CspModeStop, APP_TYPE_PDS},
    {APP_CSV_MODE, CsvModeInit, CsvModeStart, CsvModeRun, CsvModeStop, APP_TYPE_PDS},
    {APP_CST_MODE, CstModeInit, CstModeStart, CstModeRun, CstModeStop, APP_TYPE_PDS},
    {APP_DIR_ID_MODE, IdDirModeInit, IdDirModeStart, IdDirModeRun, IdDirModeStop, APP_TYPE_PDS},
    {APP_ELEC_ANGLE_ID_MODE, IdElecAngleModeInit, IdElecAngleModeStart, IdElecAngleModeRun, IdElecAngleModeStop, APP_TYPE_PDS},
    {APP_ELEC_ID_MODE, IdElecModeInit, IdElecModeStart, IdElecModeRun, IdElecModeStop, APP_TYPE_PDS},
    {APP_MEC_ID_MODE, IdMecModeInit, IdMecModeStart, IdMecModeRun, IdMecModeStop, APP_TYPE_PDS},
    {APP_POLE_PAIRS_ID_MODE, IdPolePairsModeInit, IdPolePairsModeStart, IdPolePairsModeRun, IdPolePairsModeStop, APP_TYPE_PDS},
    {APP_TQ_FC_ID_MODE, IdTqFcModeInit, IdTqFcModeStart, IdTqFcModeRun, IdTqFcModeStop, APP_TYPE_PDS},
    {APP_NULL, NULL, NULL, NULL, NULL}, // 用于表示空闲状态
};

AppRun() 从中根据配置信息查找合适的函数进行调用:

// 运行应用程序
void AppRun(uint8_t hw_ready_state)
{
    // 遍历应用程序列表
    for (int i = 0; i < APP_NUM; i++)
    {
        if (kAppInfo[i].enable)
        {

            // 启动应用程序
            if (kAppInfo[i].app_state == APP_STA_READY)
            {

                if (kAppInfo[i].app_ptr->start != NULL)
                {
                    kAppInfo[i].app_result = kAppInfo[i].app_ptr->start();
                    if (kAppInfo[i].app_result == APP_RET_RUNNING)
                    { // start处于等待状态
                        kAppInfo[i].app_state = APP_STA_READY;
                    }
                    else if (kAppInfo[i].app_result == APP_RET_SUCCESS)
                    { // start成功,进入run状态
                        kAppInfo[i].app_state = APP_STA_RUNNING;
                    }
                    else if (kAppInfo[i].app_result == APP_RET_FAIL)
                    { // start失败,进入错误状态
                        kAppInfo[i].app_state = APP_STA_ERROR;
                    }
                }
            }

            // 运行应用程序
            if (kAppInfo[i].app_state == APP_STA_RUNNING)
            {
                if (kAppInfo[i].app_ptr->run != NULL)
                {
                    // 回读PWM准备好状态,准备好则运行APP 否则等待处于 READY 状态
                    if (hw_ready_state)
                    {
                        kAppInfo[i].app_result = kAppInfo[i].app_ptr->run();
                        if (kAppInfo[i].app_result == APP_RET_RUNNING)
                        {
                            kAppInfo[i].app_state = APP_STA_RUNNING;
                        }
                        else if (kAppInfo[i].app_result == APP_RET_SUCCESS)
                        {
                            kAppInfo[i].app_state = APP_STA_SUCCESS;
                        }
                        else if (kAppInfo[i].app_result == APP_RET_FAIL)
                        {
                            kAppInfo[i].app_state = APP_STA_ERROR;
                        }
                    }
                    else
                    {
                        kAppInfo[i].app_state = APP_STA_READY;
                    }
                }
            }

            // 停止应用程序
            if ((kAppInfo[i].app_state == APP_STA_SUCCESS) || (kAppInfo[i].app_state == APP_STA_ERROR))
            {
                if (kAppInfo[i].app_ptr->stop != NULL)
                {
                    kAppInfo[i].app_ptr->stop();
                }
                kAppInfo[i].enable = false;           // 禁用应用程序
                kAppInfo[i].app_state = APP_STA_IDLE; // 设置应用程序状态为空闲
            }
        }
    }
}

3.2.2 PvModeRun()

如果我们希望配置成速度模式 (PV) 经过 AppRun() 的调用,便会来到 PvModeRun() 函数中,其功能就是根据配置的速度要求传入到 SpeedTrajectoryPlanningHandle() 速度轨迹规划函数中。

AppResult PvModeRun() {
  kPvMode.now_Controlword = (APP_CONTROL_WORD)get_app_Controlword();

  if (kPvMode.now_Controlword == APP_CTRL_ENABLE) {
    if (!get_app_Emergency_brake_requested()) {
      if (kPvMode.pre_Controlword == APP_CTRL_DISABLE) {
        // 重新使能后初始化规划器
        SpeedTrajectoryPlanningInit();
      }

      kPvMode.velocity_target = get_app_Target_velocity();

      // 规划速度为0的情况:1. 暂停指令生效 2. 抱闸状态不为松闸
      if (get_app_Halt_running_cmd() == true ||
          get_app_Brake_state() != BRAKE_STATE_RELEASED) {
        kPvMode.traj.speed_tar_p = 0;
      } else {
        kPvMode.traj.speed_tar_p = kPvMode.velocity_target *
                                   get_app_Motor_rpm_2_pps() *
                                   get_app_Reduction_ratio();
      }

      // 轮廓加速度减速度切换 目标速度绝对值 大于 当前规划速度绝对值加速
      // 否则则加速
      if (MATH_ABS(kPvMode.velocity_target) >
          MATH_ABS(get_app_Velocity_demand_value())) {
        kPvMode.traj.acc = get_app_Profile_acceleration() *
                           get_app_Motor_rpm_2_pps() *
                           get_app_Reduction_ratio();
      } else {
        kPvMode.traj.acc = get_app_Profile_deceleration() *
                           get_app_Motor_rpm_2_pps() *
                           get_app_Reduction_ratio();
      }
    } else {
      set_app_Controlword(
          APP_CTRL_EMERGENCY_BRAKE); // 强制进入紧急停车(QuickStop)状态
      kPvMode.now_Controlword = APP_CTRL_EMERGENCY_BRAKE;
    }
  }

  // 紧急停车(QuickStop)处理
  if (kPvMode.now_Controlword == APP_CTRL_EMERGENCY_BRAKE) {
    kPvMode.traj.speed_tar_p = 0; // 如果处于急停状态,规划速度为0
    kPvMode.emergency_brake_mode = get_app_Quick_stop_option_code();
    if (kPvMode.emergency_brake_mode == EMERGENCY_BRAKE_MODE_QUICK_STOP_RAMP ||
        kPvMode.emergency_brake_mode ==
            EMERGENCY_BRAKE_MODE_QUICK_STOP_RAMP_ENABLE ||
        kPvMode.emergency_brake_mode == EMERGENCY_BRAKE_MODE_CURRENT_LIMIT ||
        kPvMode.emergency_brake_mode ==
            EMERGENCY_BRAKE_MODE_CURRENT_LIMIT_ENABLE) {
      kPvMode.traj.acc = get_app_Quick_stop_deceleration() *
                         get_app_Motor_rpm_2_pps() * get_app_Reduction_ratio();
    } else if (kPvMode.emergency_brake_mode != EMERGENCY_BRAKE_MODE_DISABLED) {
      // TODO: 电压限制急停先不实现按正常减速斜率停机
      kPvMode.traj.acc = get_app_Profile_deceleration() *
                         get_app_Motor_rpm_2_pps() * get_app_Reduction_ratio();
    }
    // 在急停后失能电机模式下,检测到零速后电机失能
    if (kPvMode.emergency_brake_mode <= EMERGENCY_BRAKE_MODE_VOLTAGE_LIMIT) {
      kPvMode.check_status_val = (CheckStatusVal_t)app_get_check_status_val();
      if (kPvMode.emergency_brake_mode == EMERGENCY_BRAKE_MODE_DISABLED ||
          kPvMode.check_status_val.bits.velocity_zero == true) {
        set_app_Controlword(APP_CTRL_DISABLE);
        kPvMode.now_Controlword = APP_CTRL_DISABLE;
      }
    }
  }

  if (kPvMode.now_Controlword != APP_CTRL_DISABLE) {
    SpeedTrajectoryPlanningHandle(&kPvMode.traj);
  }

  kPvMode.pre_Controlword = kPvMode.now_Controlword;

  AppVelocityTargetReachedStateCheck();

  return APP_RET_RUNNING;
}

3.2.2 SpeedTrajectoryPlanningHandle()

在速度轨迹规划函数中直接调用 linear_trajectory_planning 模型:

// 速度轨迹规划器处理函数
void SpeedTrajectoryPlanningHandle(SPEED_TRAJECTORY_DATA *speed_traj_data)
{
    int8_T state_now = 0;
    speed_traj_data->period_s = axis->pmsm_config.tp_s;

    linear_trajectory_planning(&speed_traj_data->speed_tar_p,
                               &speed_traj_data->acc,
                               &speed_traj_data->period_s,
                               &axis->pos_speed_ctl_input.speed_tar_p_s,
                               &state_now, &(kSpeedTrajDw));
}

3.2.3 linear_trajectory_planning 模型

当系统收到一个新的目标指令 traj_cmd 时,如果直接让当前状态 traj_now 跳变过去,可能会导致硬件 (比如电机、执行机构) 受到剧烈冲击。这个函数的任务就是让当前状态以一个恒定的速度(slope,斜率) 和固定的步长 (dt_s,控制周期),一步一步地、平滑地走向目标值。

输入参数定义:

  • traj_cmd:目标轨迹指令 (你要去哪里) 
  • traj_now:当前轨迹状态 (你现在在哪里) 
  • slope:允许的最大变化率 (你走多快) 
  • dt_s:控制周期/步长 (每次走多久,通常是系统的采样时间,比如 0.01秒) 

输出参数定义:

  • traj_last:输出的速度
  • state_now:当前是否完成规划

linear_trajectory_planning 模型外观:

linear_trajectory_planning 内的代码:

function [traj_last, state_now] = fcn(traj_cmd, traj_now, slope, dt_s)
 
% state_now  规划状态  
% 0 初始状态
% 1 规划完成
% 2 规划中

traj_last_temp = single(0);
state_now_temp = int8(0);

if traj_now > traj_cmd
    traj_last_temp = traj_now - slope * dt_s;
    state_now_temp = int8(2);
    if traj_last_temp < traj_cmd
        traj_last_temp = traj_cmd;
        state_now_temp = int8(1);
    end
else
    traj_last_temp = traj_now + slope * dt_s;
    state_now_temp = int8(2);
    if traj_last_temp > traj_cmd
        traj_last_temp = traj_cmd;
        state_now_temp = int8(1);
    end

end

traj_last = traj_last_temp;
state_now = state_now_temp;

3.3 ModeRun() 系列逻辑

3.3.1 ModeRun()

这个函数位于整个电机状态机的运行阶段。它的核心作用是查表并派单。

根据当前系统配置的工作模式 (axis->motor_ctl_sm_config.mode),通过一个巨大的 switch-case 语句,调用对应的底层执行步 (Step) 函数。

例如:

  • 如果当前是“位置模式”,它就调用 PositionStep()
  • 如果当前是“速度模式”,它就调用 SpeedStep()
  • 如果当前是“转矩摩擦辨识模式”,它就调用 TqFcIdStep()
  • 等等
// 位置环内部模式运行
static void ModeRun(Axis *const axis, AxisDw *const axis_dw)
{
   // TODO:增加模式切换 和 当前模式显示处理
   axis->motor_ctl_sm_output.mode_now = axis->motor_ctl_sm_config.mode;
   switch (axis->motor_ctl_sm_config.mode)
   {
   case MOTOR_CTL_SM_MODE_POSITION: // 位置模式
      PositionStep(axis, axis_dw);
      break;
   case MOTOR_CTL_SM_MODE_SPEED: // 速度模式
      SpeedStep(axis, axis_dw);
      break;
   case MOTOR_CTL_SM_MODE_TORQUE: // 力矩模式
      TorqueStep(axis, axis_dw);
      break;
   case MOTOR_CTL_SM_MODE_TQ_FRICTION_IDENTIFICATION: // 转矩脉动与摩擦辨识
      TqFcIdStep(axis, axis_dw);
      break;
   case MOTOR_CTL_SM_MODE_MECHANICAL_IDENTIFICATION: // 机械特性辨识模式
      MecIdStep(axis, axis_dw);
      break;
   case MOTOR_CTL_SM_MODE_ELECTRIC_ANGLE_IDENTIFICATION: // 电角度辨识模式
      ElecAngleIdStep(axis, axis_dw);
      break;
   case MOTOR_CTL_SM_MODE_DIRECTION_IDENTIFICATION: // 方向辨识模式
      DirectionIdStep(axis, axis_dw);
      break;
   case MOTOR_CTL_SM_MODE_POLE_PAIRS_IDENTIFICATION: // 极对数辨识模式
      PolePairsIdStep(axis, axis_dw);
      break;
   case MOTOR_CTL_SM_MODE_ELECTRIC_IDENTIFICATION: // 电气特性辨识模式
      ElectricIdStep(axis, axis_dw);
      break;
   case MOTOR_CTL_SM_MODE_IDLE: // 空闲模式
      axis->motor_ctl_sm_output.state = 0;
      break;
   case MOTOR_CTL_SM_MODE_VOLTAGE: // 电压直接控制模式
      VoltageStep(axis, axis_dw);
      break;
   case MOTOR_CTL_SM_MODE_RECIPROCAL_MOTION: // 往复运动模式
      ReciprocalMotionStep(axis, axis_dw);
      break;
   case MOTOR_CTL_SM_MODE_REFERENCE_SIGNAL: // 参考信号模式
      ReferenceSignalStep(axis, axis_dw);
      break;
   case MOTOR_CTL_SM_MODE_MIT: // MIT模式
      MitCtlStep(axis, axis_dw);
      break;

   default:
      break;
   }
}

3.3.2 SpeedStep()

当 ModeRun() 发现当前是速度模式时,就会进入这个函数。它的作用是为速度环闭环做数据准备,并调用控制器,最后附加补偿。如果是 debug 模式则会再加一个规划。

// 速度模式
static void SpeedStep(Axis *const axis, AxisDw *const axis_dw)
{
   axis->pos_speed_ctl_config.mode = POS_SPEED_CTL_MODE_SPEED; // 纯速度控制

#ifdef MOTOR_CTL_DEBUG // 控制层调试模式,加入规划器
   PosSpeedCtlInput pos_speed_ctl_input = axis->pos_speed_ctl_input;

   linear_trajectory_planning(&axis->pos_speed_ctl_input.speed_tar_p_s,
                              &axis->pos_speed_ctl_input.acc_tar_p_ss,
                              &axis->pmsm_config.tp_s,
                              &pos_speed_ctl_input.speed_tar_p_s,
                              &kSpeedTrajStateNowTest, &(kSpeedTrajDwTest));

   pos_speed_ctl_pip(
       &pos_speed_ctl_input,
       &axis->pos_speed_ctl_config,
       &axis->pos_speed_ctl_output,
       &(axis_dw->pos_speed_ctl_pip_InstanceData.rtdw));
#else
   axis->pos_speed_ctl_input.pos_tar_p = axis->pos_speed_ctl_input.pos_now_p; // 将位置目标等于当前值,位置误差清零

   pos_speed_ctl_pip(
       &axis->pos_speed_ctl_input,
       &axis->pos_speed_ctl_config,
       &axis->pos_speed_ctl_output,
       &(axis_dw->pos_speed_ctl_pip_InstanceData.rtdw));
#endif

   // 转矩脉动与摩擦补偿
   TqFcComStep(axis, axis_dw);

   // 指令电流滤波
   CommandCurrentFilter(axis, axis_dw);
}

3.3.3 pos_speed_ctl_pip()

由 Simulink 模型生成的地方的速度环函数:

/* Output and update for referenced model: 'pos_speed_ctl_pip' */
void pos_speed_ctl_pip(const PosSpeedCtlInput *rtu_input, const
                       PosSpeedCtlConfig *rtu_config, PosSpeedCtlOutput
                       *rty_output, pos_speed_ctl_pip_DW_f *localDW)
{
    ...
}

input、output 和 config 的结构体如下:

typedef struct
{
    /* 目标位置 */
    int64_T pos_tar_p;

    /* 当前位置 */
    int64_T pos_now_p;

    /* 最大目标q轴电流 */
    real32_T iq_max_A;

    /* 目标速度 */
    real32_T speed_tar_p_s;

    /* 目标加速度 */
    real32_T acc_tar_p_ss;

    /* 当前转速 */
    real32_T speed_now_rad_s;

    /* 目标位置偏置 */
    int64_T position_offset_p;

    /* 目标速度偏置 */
    real32_T speed_offset_p_s;
}

PosSpeedCtlInput;
typedef struct
{
    /* 目标q轴电流 */
    real32_T iq_tar_A;

    /* 当前位置控制误差 */
    int32_T pos_ctl_error_p;

    /* 负载扰动补偿电流 */
    real32_T dob_iq_com_A;
}

PosSpeedCtlOutput;
typedef struct
{
    /* 加速度前馈系数 */
    real32_T aff;

    /* 速度前馈系数 */
    real32_T vff;

    /* 编码器分辨率倒数 */
    real32_T enc_line_inv_p_n;

    /* 惯性系数 */
    real32_T j_kt;

    /* 速度环积分系数 */
    real32_T ki_s;

    /* 位置环比例系数 */
    real32_T kp_p;

    /* 速度环比例系数 */
    real32_T kp_s;

    /* 最大目标速度限制 */
    real32_T speed_max_rad_s;

    /* 位置速度环运行周期 */
    real32_T tp_s;

    /* 运行模式 */
    real32_T mode;

    /* 负载扰动观测器带宽 */
    real32_T dob_wn_Hz;

    /* 负载扰动观测使能开关 */
    uint8_T dob_enable;

    /* 速度环动态积分使能 */
    uint8_T dynamic_integration_enable;
}

PosSpeedCtlConfig;

3.4 CurrentCtlLoopTask() 系列逻辑

3.4.1 CurrentCtlLoopTask()

// 位置环中断调用
void PosSpeedCtlLoopTask(Axis *const axis, AxisDw *const axis_dw)
{
   uint32_T error_code = 0;

   // 运行电机端位置传感器数据处理
   motor_pos_sensor(&axis->motor_pos_sensor_input,
                    &axis->motor_pos_sensor_config,
                    &axis->motor_pos_sensor_output,
                    &(axis_dw->motor_pos_sensor_InstanceData.rtdw));

   axis->pos_speed_ctl_input.pos_now_p = axis->motor_pos_sensor_output.enc_sum_p; // 获取电机端当前绝对位置

   // 运行负载端位置传感器数据处理
   load_pos_sensor(&axis->load_pos_sensor_input,
                   &axis->load_pos_sensor_config,
                   &axis->load_pos_sensor_output,
                   &(axis_dw->load_pos_sensor_InstanceData.rtdw));

   // 运行速度观测器 进行速度估计
   axis->speed_obs_pll_input.pos_now_p = axis->pos_speed_ctl_input.pos_now_p;

   speed_obs_pll(&axis->speed_obs_pll_input, &axis->speed_obs_pll_config,
                 &axis->speed_obs_pll_output,
                 &(axis_dw->speed_obs_pll_InstanceData.rtdw));

   axis->pos_speed_ctl_input.speed_now_rad_s = axis->speed_obs_pll_output.ev_rad_s; // 速度观测器输出

   // 保护检测  更新状态机错误代码
   protect_check(&axis->current_ctl_input.iabc_now_A[0], &axis->current_ctl_input.dc_bus_now_V,
                 &axis->current_ctl_input.driver_temp,
                 &axis->pos_speed_ctl_output.pos_ctl_error_p,
                 &axis->pos_speed_ctl_input.speed_now_rad_s,
                 &axis->motor_ctl_sm_config,
                 &error_code);

   // 状态机切换
   // 错误状态
   if (error_code != MOTOR_CTL_SM_NONE_ERROR) // 有错误 进入错误状态
   {
      axis->motor_ctl_sm_output.state = MOTOR_CTL_SM_STATE_ERROR;
      axis->motor_ctl_sm_output.error = error_code;
   }

   if (axis->motor_ctl_sm_output.state == MOTOR_CTL_SM_STATE_ERROR)
   {
      if (axis->motor_ctl_sm_input.motor_enable == MOTOR_CTL_SM_MOTOR_CLEAR_ERROR &&
          axis->motor_ctl_sm_output.motor_enable_last == MOTOR_CTL_SM_MOTOR_DISABLE) // 清除错误  0->3 清除错误
      {
         axis->pos_speed_ctl_output.pos_ctl_error_p = 0;               // 清除位置跟随误差
         axis->motor_ctl_sm_output.state = MOTOR_CTL_SM_STATE_DISABLE; // 进入空闲状态
         axis->motor_ctl_sm_output.error = MOTOR_CTL_SM_NONE_ERROR;    // 清除错误
      }
      axis->motor_ctl_sm_output.motor_enable_last = axis->motor_ctl_sm_input.motor_enable;
      return; // 没有清除错误指令 直接退出
   }

   // 其他状态下 失能指令会立即进入失能状态
   if (axis->motor_ctl_sm_input.motor_enable == MOTOR_CTL_SM_MOTOR_DISABLE) // 失能电机状态
   {
      axis->motor_ctl_sm_output.state = MOTOR_CTL_SM_STATE_DISABLE; // 进入失能状态
   }

   // 失能状态
   if (axis->motor_ctl_sm_output.state == MOTOR_CTL_SM_STATE_DISABLE)
   {
      // 电流环 进入 电压控制  输出电压为 0
      axis->current_ctl_input.idq_tar_A[0] = 0.0f;
      axis->current_ctl_input.idq_tar_A[1] = 0.0f;
      axis->current_ctl_input.mode = 0; // 电压直接控制模式
      axis->current_ctl_input.uabc_tar_V[0] = 0.0f;
      axis->current_ctl_input.uabc_tar_V[1] = 0.0f;
      axis->current_ctl_input.uabc_tar_V[2] = 0.0f;
      axis->pos_speed_ctl_output.pos_ctl_error_p = 0; // 清除位置跟随误差
   }

   // 空闲状态
   if (axis->motor_ctl_sm_output.state == MOTOR_CTL_SM_STATE_DISABLE) // 空闲状态等待使能指令
   {
      // 使能状态  0->1 电机进入使能状态
      if (axis->motor_ctl_sm_input.motor_enable == MOTOR_CTL_SM_MOTOR_ENABLE &&
          axis->motor_ctl_sm_output.motor_enable_last == MOTOR_CTL_SM_MOTOR_DISABLE)
      {
         ModeCheckInInit(axis, axis_dw);                              // 使能前初始化
         axis->motor_ctl_sm_output.state = MOTOR_CTL_SM_STATE_ENABLE; // 进入使能状态
      }
   }

   // 使能状态 且 硬件准备就绪
   if (axis->motor_ctl_sm_output.state == MOTOR_CTL_SM_STATE_ENABLE)
   {
      if (axis->motor_ctl_sm_input.hw_ready_state)
      {
         ModeRun(axis, axis_dw); // 模式运行
      }
   }

   // 记录上个周期指令和状态
   axis->motor_ctl_sm_output.motor_enable_last = axis->motor_ctl_sm_input.motor_enable;
}

3.4.2 motor_pos_sensor()

3.4.3 load_pos_sensor()

3.4.4 speed_obs_pll()

3.4.5 protect_check()

21 硬件工程师笔面试高频考点真题解析——单片机 本文系统介绍了单片机的核心概念与应用知识,包括:单片机定义与选型要点(性能、存储、外设等);核心参数(主频、I/O、通信接口等);最小系统组成(电源、复位、晶振、BOOT);常见外设(GPIO、ADC、定时器等);GPIO工作模式(输入/输出);死机原因分析(硬件干扰、软件错误等);上电故障排查流程(电源、时钟、程序等);与DSP、FPGA的对比(控制vs运算vs可编程);常用通信总线特点(UART、I2C、SPI、CAN);看门狗功能(超时复位防死机)。内容涵盖单片机硬件设计关键要点,适合嵌入式开发者参考 阅读详情

相关推荐

NV MCU与SoC的交互

MCU通过IST_REQ信号触发SoC执行IST,由IST_DONE_N通知完成,SOC_RESET_N由MCU控制。MCU与SoC间通过通信通道(如以太网)传输IST结果。热/电压监测通过I2C实现,阈值由MCU初始设置、SoC动态更新,异常可中止IST。客户应用可在检测严重错误时通过IST管理器中止IST,相关原因与状态将被记录。

liu-yonggang的专栏 55

YuanHub 源码分析【六】MIT 模式

本文介绍了MIT控制模式在机器人关节控制中的应用与实现。MIT模式是一种混合集成扭矩控制方法,通过模拟弹簧-阻尼系统实现精准的位置和速度控制。文章首先阐述了MIT的基本原理,包括位置比例系数(Kp)作为虚拟刚度和速度阻尼系数(Kd)作为虚拟阻尼的物理意义。随后详细解析了控制系统的代码架构,重点介绍了AppMotionParam结构体、AppRun()函数逻辑以及MitModeRun()等核心控制函数。在仿真分析部分,文章展示了MIT控制模型的实现细节,包括Kp环和Kd环的计算方法,以及如何将控制输出转换为电

nullccc的博客 1292

DexProtector内存校验绕过与DEX提取实战

DEX文件是Android应用的核心执行单元,其动态加载与运行时保护机制直接关系到App安全强度。DexProtector作为主流商业加固方案,摒弃传统文件级防护,转而依托ART虚拟机下的匿名内存映射段(anonymous mmap)构建多维校验体系——涵盖字节码完整性校验、内存页属性检测及运行时环境感知。其技术价值在于将防护锚点从静态APK迁移至动态内存态,大幅提升逆向门槛。典型应用场景包括金融类App的防篡改加固、游戏客户端的反外挂保护,以及政企应用的合规性安全增强。本文聚焦DexProtector v

weixin_30315435的博客 714

YuanHub 源码分析【二】全局变量与调试

本文介绍了YuanHub项目中Simulink控制器模型的结构体参数体系,重点解析了电流环、位置速度环等核心控制模块的参数配置。主要内容包括: Simulink模型采用模块化设计,每个功能对应独立模型文件,包含input/config/output三个结构体接口 详细说明了电流环控制结构体(CurrentCtlConfig/Input/Output)的参数定义,包括PI增益、死区补偿等关键参数 介绍了位置速度环、编码器、速度观测器等模块的结构体配置 阐述了伺服系统的参数数据库体系,包含指令层、反馈层、配置层

nullccc的博客 497

ISO 15118-3协议栈开发避坑指南:从EvseSlac状态机设计看电动汽车充电握手难题

本文深入剖析了ISO 15118-3协议栈开发中的核心挑战,特别是SLAC握手流程的实现难点。以Everest项目的EvseSlac状态机为例,详细解读了从状态机设计、多厂商PLC芯片兼容性处理,到严苛的时序与超时控制等关键工程实践,为开发者提供了一套超越协议文档的、可直接落地的避坑指南和软件实现方案。

jump7的博客 269

从压电传感器到示波器:手把手教你用CA3140搭建电荷放大器(含PCB布局与调零技巧)

本文详细介绍了如何使用CA3140运算放大器搭建专业级电荷放大器,从压电传感器信号特性分析到PCB布局与调零技巧,全程实战演示。重点解析了CA3140的低通滤波器设计、反馈网络参数计算及抗干扰增强方案,帮助电子爱好者高效实现微弱电荷信号的稳定转换与测量。

weixin_31355483的博客 598

基本定时器(TIM6/TIM7)

基本定时器最简单,就干一件事:到点了触发中断。定时器本质就是计数器,每个时钟脉冲加1,数到ARR就溢出、产生中断、清0重来。三个核心寄存器:PSC(预分频器):对输入时钟分频CNT(计数器):从0往上数ARR(自动重装载值):数到这个值就溢出,CNT清0,产生更新中断公式:  定时周期 = (PSC+1) × (ARR+1) / 定时器时钟  更新事件频率 = 定时器时钟 / ((PSC+1) × (ARR+1)) 例:TIM6时钟84MHz,要定时0.5秒(2

2401_89890233的博客 260

LabVIEW Modbus TCP通信避坑指南:解决连接中断、服务器异常与自动重连问题

本文详细解析了LabVIEW环境下Modbus TCP通信的常见问题,包括连接中断、服务器异常等,并提供了自动重连机制的实现方案。通过优化错误处理、超时参数和资源管理,帮助开发者构建更稳定的工业级Modbus TCP客户端,显著提升通信可靠性。

weixin_42525211的博客 425

7-2 按键控制LED流水灯(SDCC + Notepad++ + STC-ISP/普中ISP)

本节基于上节定时器T0设计,实现按键控制LED流水灯。通过配置12MHz晶振下16位定时器模式,初值设为64536(0xFC18),每1毫秒溢出一次,替代原有Delay方法。启用定时器中断,设置TR0启动,TF0标志自动置位触发中断,配合外部按键控制流水灯状态,提升系统实时性与效率。

xunyan6234的博客 81

集成电路设计学习笔记(二)基于IC617反相器设计之原理图绘制

博主的博客网站 欢迎大家访问! ccbirds.cn ccbirds.github.io 本文主要记录在IC617中原理图的绘制和schematic的使用方法。 新建library 要画设计自己的集成电路,需要建一个自己的库,方便对自己设计文件的管理。 打开library manager,file 下NEW下Library 选择库的保存路径,一般默认即可,并在name中填入自己库的名字。...

ccbirds 7376

Vlan的封装模式和端口讲解(Access、Trunk、Hypbrid端口)

Vlan(Virtual Local Area Network)虚拟局域网,将一个物理的LAN在逻辑上划分为多个广播域,在交换机上实现广播域隔离(增强局域网的安全性、限制广播风暴、简化网络管理)Vlan通过将二层数据帧打上Vlan标签(打上Vlan标签也就是封装Vlan),正常情况下,不同Vlan Tag的数据包是无法二层直接互访的,以此来实现广播域的隔离可以理解为一个Vlan对应一个广播域,同一Vlan内的主机可以二层直接通信,不同Vlan间的主机无法二层直接通信。

m0_49864110的博客 5646

TMS320F28335 学习笔记(二):系统时钟、PLL 与看门狗

本文系统介绍F28335的时钟架构与看门狗应用,涵盖外部晶振、PLL倍频生成系统时钟(SYSCLKOUT)、高速/低速外设时钟分频配置,以及看门狗的启用、关闭与喂狗机制。强调时钟配置准确性对延时、PWM、通信等外设的重要性,指出常见错误如晶振不匹配、未等待PLL锁定、随意修改PLL或喂狗周期过长等问题,并提供初始化框架与验证方法,帮助开发者正确配置时钟系统,确保程序稳定运行。

2403_83044722的博客 367

鸿蒙应用发布实战:应用热更新的实现

在移动应用开发领域,应用更新是持续交付价值的关键环节。传统的应用更新方式需要用户手动下载完整安装包,不仅用户体验差,而且更新率往往不理想。热更新技术(也称为OTA更新)允许应用在不重新安装的情况下动态更新部分功能,极大提升了用户体验和迭代效率。本文专注于鸿蒙操作系统下的应用热更新实现方案,涵盖从原理到实践的完整知识体系。鸿蒙热更新的技术架构差分更新算法实现动态部署机制实际项目中的最佳实践安全与性能优化策略首先介绍鸿蒙热更新的背景和核心概念深入分析技术原理和算法实现。

操作系统内核探秘的博客 965

代码小白笔记:江科大 51 单片机 LCD1602 课程详解

本文为单片机新手梳理江科大51课程中LCD1602的驱动逻辑,聚焦“看懂代码”而非深究寄存器。通过解析接线、底层函数(延时、写命令/数据)、初始化流程及光标定位,详解字符、字符串、数字(十进制、十六进制、二进制)的显示原理,核心在于理解“RS控制指令/数据”、“地址映射规则”及“数字转字符”的位运算技巧,帮助读者快速上手并自定义显示内容。

2301_81429079的博客 338

基于 FPGA 的 CNN 卷积神经网络之池化层 Verilog 实现

池化层是卷积神经网络(CNN)中的关键组件,用于降低特征图的维度,减少计算量和防止过拟合。常见的池化操作包括最大池化和平均池化。FPGA 提供了高效的并行处理能力,可以加速这些操作。池化层在 CNN 中扮演重要角色,通过降低数据维度提升计算效率。在 FPGA 上实现池化层,可以充分利用硬件资源实现高效的并行计算,有助于提高实时应用中的推理速度。

走向CTO的路上... 1256

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

MCU 启动流程解析:从 SRAM 到 DDR

MCU 的性能瓶颈通常不在 DDR 带宽,而在访问延迟与确定性。因此,关键代码、中断向量、栈以及功能安全数据应放在片内 SRAM;大块传感器数据缓冲、堆和冷数据则放在 DDR。启动流程的核心可以概括为三步:Boot ROM 搬运、Remap 切换、SRAM 执行。

Where there is life, there is hope. 290

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

XZ6273H 100V低功耗高压LDO硬件设计与实测体验

这款LDO提供SOT23-3、SOT89-3和TO92三种通用封装,不同场景下可以灵活选择:做小型化的PCB节点时用SOT23-3,板卡面积能控制得非常小;常规工业板卡选SOT89-3,贴片焊接方便,自带的焊盘也能提供基础散热能力;从实际落地的情况来看,它最适配两类场景:一类是汽车电子里的休眠值守子模块,车辆熄火后仍能保持极低的待机功耗,不会过度消耗电瓶电量;另一类是户外无人值守的通信远端节点,长期运行下的低功耗表现能大幅降低设备的供电维护成本。

koce13544100678的博客 231

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

MCU的ADC采样怎么配

一块采集板,室温下读电池电压挺准,一上高低温箱就差出0.3V。软件反复查了三遍,没改一行代码,最后发现是参考电压选错了——电源纹波直接灌进了测量基准。ADC这东西,代码就那么几行,坑全在配置里。这篇拿ATmega16当例子,把采样链路从头讲清:参考电压怎么选、时钟为什么卡在50到200kHz、结果为什么要讲究读取顺序。这些规则不只对AVR成立,换成车规MCU,思路一样。

qq_43241255的博客 194

HAL库基本定时器框架分析

这部分是在main.c里面调用的,使能定时器外设和中断位。实时真正干活的操作在中断处理函数里进行。流程1 定时器外设初始化和中断配置。流程2 使能定时器中断相关位。流程3 中断处理函数。

tecoes的博客 22

PHY内部环回通过,能证明网口整条链路正常吗?

PHY内部环回通过,只能证明该测试路径在记录条件下工作正常,不能直接证明变压器、RJ45、网线和对端组成的整条链路正常。采购收到“环回合格”报告时,应先确认数据从哪里发出、在哪里折返、又在哪里检查。

Voohu_Elsa的博客 189

从FT 93k测试机角度看如何测试芯片的VOH/VOL/VIH/VIL,通俗易懂的方式介绍

FT 93k测试中,VIH/VIL与VOH/VOL的核心是验证芯片“听清”与“说清”的能力。VIH/VIL测试芯片输入灵敏度,通过施加边界电压并观察功能响应;VOH/VOL则在负载下测量输出电平,模拟真实工作状态。测试需严格遵循规格书的负载电流条件,确保信号可靠传输,避免灰色地带干扰。本质是精准复现测试条件,实现“听话的对话者”与“挑剔的听众”的双重角色。

Wang20122013的博客 227

Arduino-MEGA-ESP单片机理论课笔记-06温湿度-07时间-延时定时-2026

本文解析了DHT11温湿度传感器的Arduino代码,逐行讲解了库引入、引脚定义、传感器初始化及数据读取流程。重点说明了delay(1000)导致阻塞的问题,并提出使用millis()实现非阻塞定时,将采样间隔改为2秒,提升程序响应性。同时建议增加isnan()错误检测、优化串口输出格式,确保数据稳定可靠。

zhangrelay的专栏 363

认识元器件 · 单片机(MCU/MPU/SOC) · 实战篇16

VBUS检测用于识别USB主机是否存在,自供电设备强制检测,采用PA9引脚(5V耐压)通过分压或MOSFET方案实现。分压电阻取高阻值(如82k+33k),确保分压后电压≤4V,符合GPIO输入电平要求;当GPIO耐压条件不足时,改用N沟道MOSFET(如2N7002)检测,适用于高电压场景(如5–48V)。三电阻加焊桥可覆盖1.8V/3.3V双档,但量产建议使用标准封装。需避免MCU未上电时5V VBUS接入PA9,以防损坏器件。

一只天天被叨叨的小可爱 207

哪些国产单片机厂家提供FAE技术支持?英锐恩等对比

大厂FAE资源通常优先服务大客户,中小批量项目往往只能找代理商或看论坛。如果你需要,推荐:收到请求当日或次日回复,必要项目24小时内工程师到场,还能组织芯片研发工程师一起评审问题。生态成熟、资料丰富;通过代理商体系提供支持。

2601_95079068的博客 226

【电感专题2:电感的感抗、电路的阻抗、电流的滞后】

本文系统阐述了电感的感抗特性及其对交流电路的影响。感抗 随频率升高而增大,阻碍电流变化,导致电流滞后电压90°。在串联RL电路中,总阻决定电流大小,频率或电感增大时阻抗上升,电流减小;反之则电流增大。仿真验证了理论计算与波形特征一致,直观展示了电流滞后于电压的相位关系。

weixin_57246604的博客 330

USB3.0读卡器为何跑不满速?CH379工程实践拆解

规范覆盖上,这颗芯片从eMMC4.4、4.5.1一直到5.0的HS200与HS400都有支持,老库存卡和新采购颗粒都能兼容,这在料源不稳定的时期尤为重要,一颗芯片同时兼容SD物理层1.0、2.0与SD3.0规范,也覆盖了MMC卡这类偏工业的存量介质。调试阶段,TEST引脚每秒输出一帧状态数据,包含USB连接速度、卡的插入与初始化状态以及上下传速率,115200bps的串口格式接一根线就能读取,产线工装可以直接用它做成品测试判断,不必反复插拔试错。这颗决定读卡器性能上限的芯片,值得从工程角度拆开看一遍。

ccbw16的博客 120
上一篇: YuanHub 源码分析【五】速度模式举例通过 Mavlink 协议配置过
下一篇: YuanHub 源码分析【六】MIT 模式
崔佳宇_
崔佳宇_ 优质创作者: 嵌入式与硬件开发技术领域 优质创作者: 嵌入式与硬件开发技术领域
博客等级 码龄10年 1万+粉丝 173原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值