156|2

258

帖子

0

TA的资源

纯净的硅(初级)

[航芯高性能MCU系列:ACM32F403开发板]5.S形加减速实现对滑台的运动控制 [复制链接]

本帖最后由 xld0932 于 2025-4-16 09:05 编辑

1.前言

    通过学习正点原子实现步进电机进行S形加减速控制,实现对滑台的运动控制

 

2.S 形曲线加减速的简介(《摘录于DMF407电机控制专题教程》)

    还是以梯形加减速章节提到的模型为例,如果滑块从启动速度到目标速度的加减速不是以固定的比例进行加速/减速,而在加减速的变化过程中速度曲线呈现一个英文字母“S”形的,我们称之为 S 形加减速算法。则上述将这个过程描述为如下图所示,

9.png

    可以获知 OA 段其实就是滑块的加速部分、 AB 则是匀速部分, BC 则是减速部分。

⚫ 在 OA 加速过程中,速度刚开始是缓慢增加,后来增加得越来越快,而在中点时刻,增加又有所放慢,但依然继续增加逼近设定的速度。实际这一阶段又分成了三个阶段

⚫ 在 AB 匀速过程中,加速到设定速度之后,以设定速度匀速步进;

⚫ 在 BC 减速部分中,以设定的速度开始按照加速度段的变化规律做减速变化,直到速度降至 0 后停止。

    前面我们有提到梯形加减速的缺点,梯形加减速在启动、停止和高速运动的过程中会产生很大的冲击力振动和噪声,所以多数会应用于简单的定长送料的应用场合中,例如常见的3D 打印机使用的就是梯形加减速算法; 但是相比较 S 形加减速在启动停止以及高速运动时的速度变化的比较慢,导致冲击力噪音就很小,但这也决定了他在启动停止时需要较长的时间,所以多数适用于精密的工件搬运与建造。

 

S 形曲线加减速的的原理分析:

    实际上要实现 S 型可以采用的方法有很多,在传统的 S 形曲线加减速算法中,它包括七个运动阶段: 加加速阶段, 恒加速阶段, 减加速阶段, 恒速阶段, 加减速阶段,恒定减速阶段和减减速阶段。
S 曲线加减速七段式模型:

10.png

S 曲线加减速五段式模型:

11.png

S 曲线加减速各阶段和脉冲的关系图解:

12.png

S 曲线加减速各阶段速度解析:

13.png

 

3.S形加减速的控制实现

3.1.相关宏定义及类型声明

#ifndef __STEPPER_H
#define __STEPPER_H

#ifdef __cplusplus
extern "C" {
#endif

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "fxx_std.h"

enum STEPPER_STATE
{
    STOP  = 0,                         /* 加减速曲线状态:停止*/
    ACCEL = 1,                         /* 加减速曲线状态:加速阶段*/
    DECEL = 2,                         /* 加减速曲线状态:减速阶段*/
    RUN   = 3                          /* 加减速曲线状态:匀速阶段*/
};
enum STEPPER_DIRECTION
{
    CCW = 0,                           /* 逆时针 */
    CW  = 1                            /* 顺时针 */
};
enum STEPPER_EN
{
    EN_ON  = 0,                        /* 失能脱机引脚 */
    EN_OFF = 1                         /* 使能脱机引脚 使能后电机停止旋转 */
};

typedef struct
{
    int32_t  vo;                       /*  初速度 单位 step/s */
    int32_t  vt;                       /*  末速度 单位 step/s */
    int32_t  accel_step;               /*  加速段的步数单位 step */
    int32_t  decel_step;               /*  加速段的步数单位 step */
    float   *accel_tab;                /*  速度表格 单位 step/s 步进电机的脉冲频率 */
    float   *decel_tab;                /*  速度表格 单位 step/s 步进电机的脉冲频率 */
    float   *ptr;                      /*  速度指针 */
    int32_t  dec_point;                /*  减速点 */
    int32_t  step;
    int32_t  step_pos;
} speed_calc_t;

typedef enum
{
    STATE_ACCEL = 1,                                              /* 电机加速状态 */
    STATE_AVESPEED = 2,                                           /* 电机匀速状态 */
    STATE_DECEL = 3,                                              /* 电机减速状态 */
    STATE_STOP  = 0,                                              /* 电机停止状态 */
    STATE_IDLE  = 4                                               /* 电机空闲状态 */
} motor_state_typedef;

#define MICRO_STEP              8                                 /* 步进电机驱动器细分数 */
#define TIM_FREQ                180000000U                        /* 定时器主频 */
#define MAX_STEP_ANGLE          0.1125                            /* 最小步距(0.9/MICRO_STEP) */
#define PAI                     3.1415926                         /* 圆周率*/
#define FSPR                    400                               /* 步进电机单圈步数 */
#define T1_FREQ                 (TIM_FREQ / 80)                   /* 频率ft值 */
#define SPR                     (FSPR * MICRO_STEP)               /* 旋转一圈需要的脉冲数 */

#define ROUNDPS_2_STEPPS(rpm)  ((rpm) * SPR / 60)                 /* 根据电机转速(r/min),计算电机步速(step/s) */
#define MIDDLEVELOCITY(vo, vt) (((vo) + (vt)) / 2)                /* S型加减速加速段的中点速度  */
#define INCACCEL(vo, v, t)     ((2 * ((v) - (vo))) / pow((t), 2)) /* 加加速度:加速度增加量   V - V0 = 1/2 * J * t^2 */
#define INCACCELSTEP(j, t)     (((j) * pow((t), 3)) / 6.0f)       /* 加加速段的位移量(步数)  S = 1/6 * J * t^3 */
#define ACCEL_TIME(t)          ((t) / 2)                          /* 加加速段和减加速段的时间是相等的 */
#define SPEED_MIN               (T1_FREQ / (65535.0f))            /* 最低频率/速度 */

#ifndef TRUE
#define TRUE                    1
#endif
#ifndef FALSE
#define FALSE                   0
#endif

void stepper_init(uint16_t arr, uint16_t psc);
void stepmotor_move_rel(int32_t vo, int32_t vt, float AcTime, float DeTime, int32_t step); /* S型加减速运动控制函数 */
uint8_t calc_speed(int32_t vo, int32_t vt, float time);                                    /* 计算速度表 */

#ifdef __cplusplus
}
#endif

#endif

 

3.2.定时器及GPIO初始化

void TIM_OC1PreloadConfig(TIM_TypeDef *TIMx, uint16_t TIM_OCPreload)
{
    uint16_t tmpccmr1 = 0;

    tmpccmr1  = TIMx->CCMR1;

    tmpccmr1 &= (uint16_t) ~((uint16_t)0x0008);

    tmpccmr1 |= TIM_OCPreload;

    TIMx->CCMR1 = tmpccmr1;
}

void stepper_init(uint16_t arr, uint16_t psc)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    System_Module_Enable(EN_GPIOCD);

    /* Direction */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.Pin       = GPIO_PIN_9;
    GPIO_InitStruct.Mode      = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull      = GPIO_PULLUP;
    GPIO_InitStruct.Alternate = GPIO_FUNCTION_0;
    GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_WriteBit(GPIOC, GPIO_PIN_9, Bit_SET);

    /* Limit Switch */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.Pin       = GPIO_PIN_10 | GPIO_PIN_11;
    GPIO_InitStruct.Mode      = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull      = GPIO_PULLUP;
    GPIO_InitStruct.Alternate = GPIO_FUNCTION_0;
    GPIO_Init(GPIOC, &GPIO_InitStruct);

    TIM_Base_InitTypeDef TIM_Base_InitStruct;
    TIM_OC_InitTypeDef   TIM_OC_InitStruct;
    uint32_t             TIM_Clock;

    System_Module_Enable(EN_GPIOCD);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.Pin       = GPIO_PIN_8;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull      = GPIO_NOPULL;
    GPIO_InitStruct.Alternate = GPIO_FUNCTION_2;
    GPIO_Init(GPIOC, &GPIO_InitStruct);

    System_Module_Enable(EN_TIM1);

    if (System_Get_SystemClock() == System_Get_APBClock())
    {
        TIM_Clock = System_Get_APBClock();
    }
    else
    {
        TIM_Clock = System_Get_APBClock() * 2;
    }

    TIM_Base_InitStruct.Prescaler         = psc;
    TIM_Base_InitStruct.Period            = arr;
    TIM_Base_InitStruct.RepetitionCounter = 0;
    TIM_Base_InitStruct.CounterMode       = TIM_COUNTERMODE_UP;
    TIM_Base_InitStruct.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
    TIM_TimeBase_Init(TIM1, &TIM_Base_InitStruct);

    TIM_OC_InitStruct.OCMode       = OUTPUT_MODE_MATCH_TOGGLE;
    TIM_OC_InitStruct.Pulse        = 0;
    TIM_OC_InitStruct.OCPolarity   = OUTPUT_POL_ACTIVE_HIGH;
    TIM_OC_InitStruct.OCNPolarity  = OUTPUT_POL_ACTIVE_HIGH;
    TIM_OC_InitStruct.OCFastMode   = OUTPUT_FAST_MODE_DISABLE;
    TIM_OC_InitStruct.OCIdleState  = OUTPUT_IDLE_STATE_0;
    TIM_OC_InitStruct.OCNIdleState = OUTPUT_IDLE_STATE_0;
    TIM_OC1Init(TIM1, &TIM_OC_InitStruct);

    TIM_OC1PreloadConfig(TIM1, 0);

    TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE);
    TIM_ClearFlag(TIM1, TIM_IT_CC1);

    NVIC_ClearPendingIRQ(TIM1_CC_IRQn);
    NVIC_EnableIRQ(TIM1_CC_IRQn);

    TIM_Cmd(TIM1, ENABLE);

    TIM_CtrlPWMOutputs(TIM1, ENABLE);
}

 

3.3.速度表计算

/**
  * @brief        速度表计算函数
  * @param       vo,初速度;vt,末速度;time,加速时间
  * @retval      TRUE:成功;FALSE:失败
  */
uint8_t calc_speed(int32_t vo, int32_t vt, float time)
{
    uint8_t is_dec = FALSE;
    int32_t i      = 0;
    int32_t vm     = 0;                     /* 中间点速度 */
    int32_t inc_acc_stp = 0;                /* 加加速所需的步数 */
    int32_t dec_acc_stp = 0;                /* 减加速所需的步数 */
    int32_t accel_step  = 0;                /* 加速或减速需要的步数 */
    float jerk    = 0;                      /* 加加速度 */
    float ti      = 0;                      /* 时间间隔 dt */
    float sum_t   = 0;                      /* 时间累加量 */
    float delta_v = 0;                      /* 速度的增量dv */
    float ti_cube = 0;                      /* 时间间隔的立方 */
    float *velocity_tab = NULL;             /* 速度表格指针 */

    if (vo > vt)                            /* 初速度比末速度大,做减速运动,数值变化跟加速运动相同 */
    {
        /* 只是建表的时候注意将速度倒序 */
        is_dec = TRUE;                      /* 减速段 */
        g_calc_t.vo = ROUNDPS_2_STEPPS(vt); /* 转换单位 起速:step/s */
        g_calc_t.vt = ROUNDPS_2_STEPPS(vo); /* 转换单位 末速:step/s */
    }
    else
    {
        is_dec = FALSE;                /* 加速段 */
        g_calc_t.vo = ROUNDPS_2_STEPPS(vo);
        g_calc_t.vt = ROUNDPS_2_STEPPS(vt);
    }

    time = ACCEL_TIME(time);                                                   /* 得到加加速段的时间 */
    printf("time=%f\r\n", time);
    vm = (g_calc_t.vo + g_calc_t.vt) / 2;                                      /* 计算中点速度 */

    jerk = fabs(2.0f * (vm - g_calc_t.vo) / (time * time));                    /* 根据中点速度计算加加速度 */

    inc_acc_stp = (int32_t)(g_calc_t.vo * time + INCACCELSTEP(jerk, time));    /* 加加速需要的步数 */

    dec_acc_stp = (int32_t)((g_calc_t.vt + g_calc_t.vo) * time - inc_acc_stp); /* 减加速需要的步数 S = vt * time - S1 */

    /* 申请内存空间存放速度表 */
    accel_step = dec_acc_stp + inc_acc_stp;                                    /* 加速需要的步数 */

    if (accel_step % 2 != 0)                                                   /* 由于浮点型数据转换成整形数据带来了误差,所以这里加1 */
    {
        accel_step  += 1;
    }

    /* mallo申请内存空间,记得释放 */
    velocity_tab = (float *)(malloc(((accel_step + 1) * sizeof(float))));

    if (velocity_tab == NULL)
    {
        printf("\r\nno enough space, pls adjust param!");
        return (FALSE);
    }

/*
 * 目标的S型速度曲线是对时间的方程,但是在控制电机的时候则是以步进的方式控制,所以这里对V-t曲线做转换
 * 得到V-S曲线,计算得到的速度表是关于步数的速度值.使得步进电机每一步都在控制当中
 */
/* 计算第一步速度,根据第一步的速度值达到下一步的时间 */
    ti_cube  = 6.0f * 1.0f / jerk;          /* 根据位移和时间的公式S = 1/6 * J * ti^3 第1步的时间:ti^3 = 6 * 1 / jerk */
    ti       = pow(ti_cube, (1 / 3.0f));    /* ti */
    sum_t    = ti;
    delta_v  = 0.5f * jerk * pow(sum_t, 2); /* 第一步的速度 */
    velocity_tab[0] = g_calc_t.vo + delta_v;

/*****************************************************/
    if (velocity_tab[0] <= SPEED_MIN)  /* 以当前定时器频率所能达到的最低速度 */
    {
        velocity_tab[0] = SPEED_MIN;
    }

/*****************************************************/

    for (i = 1; i < accel_step; i++)
    {
        /* 步进电机的速度就是定时器脉冲输出频率,可以计算出每一步的时间 */
        /* 得到第i-1步的时间 */
        ti = 1.0f / velocity_tab[i - 1]; /* 电机每走一步的时间 ti = 1 / Vn-1 */

        /* 加加速段速度计算 */
        if (i < inc_acc_stp)
        {
            sum_t  += ti;                            /* 从0开始到i的时间累积 */
            delta_v = 0.5f * jerk * pow(sum_t, 2);   /* 速度的变化量: dV = 1/2 * jerk * ti^2 */
            velocity_tab[i] = g_calc_t.vo + delta_v; /* 得到加加速段每一步对应的速度 */

            /* 当最后一步的时候,时间并不严格等于time,所以这里要稍作处理,作为减加速段的时间 */
            if (i == inc_acc_stp - 1)
            {
                sum_t  = fabs(sum_t - time);
            }
        }
        /* 减加速段速度计算 */
        else
        {
            sum_t  += ti;                                       /* 时间累计 */
            delta_v = 0.5f * jerk * pow(fabs(time - sum_t), 2); /* dV = 1/2 * jerk *(T-t)^2 看这个逆向看减加速的图 */
            velocity_tab[i] = g_calc_t.vt - delta_v;            /* V = vt - delta_v */

            if (velocity_tab[i] >= g_calc_t.vt)
            {
                accel_step = i;
                break;
            }
        }
    }

    if (is_dec == TRUE)                /* 减速 */
    {
        float tmp_Speed = 0;

        /* 倒序排序 */
        for (i = 0; i < (accel_step / 2); i++)
        {
            tmp_Speed = velocity_tab[i];
            velocity_tab[i] = velocity_tab[accel_step - 1 - i]; /* 头尾速度对换 */
            velocity_tab[accel_step - 1 - i] = tmp_Speed;
        }

        g_calc_t.decel_tab  = velocity_tab; /* 减速段速度表 */
        g_calc_t.decel_step = accel_step;   /* 减速段的总步数 */
    }
    else /* 加速 */
    {
        g_calc_t.accel_tab  = velocity_tab; /* 加速段速度表 */
        g_calc_t.accel_step = accel_step;   /* 加速段的总步数 */
    }

    return (TRUE);
}

 

3.4.S型加减速运动

/**
  * @brief       S型加减速运动
  * @param       vo:初速度;vt:末速度;AcTime:加速时间;DeTime:减速时间;step:步数;
  * @retval      无
  */
void stepmotor_move_rel(int32_t vo, int32_t vt, float AcTime, float DeTime, int32_t step)
{
    if (calc_speed(vo, vt, AcTime) == FALSE) /* 计算出加速段的速度和步数 */
    {
        return;
    }

    if (calc_speed(vt, vo, DeTime) == FALSE) /* 计算出减速段的速度和步数 */
    {
        return;
    }

    if (step < 0)
    {
        step = -step;
        GPIO_WriteBit(GPIOC, GPIO_PIN_9, Bit_SET);
    }
    else
    {
        GPIO_WriteBit(GPIOC, GPIO_PIN_9, Bit_RESET);
    }

    if (step >= (g_calc_t.decel_step + g_calc_t.accel_step))      /* 当总步数大于等于加减速度步数相加时,才可以实现完整的S形加减速 */
    {
        g_calc_t.step = step;
        g_calc_t.dec_point = g_calc_t.step - g_calc_t.decel_step; /* 开始减速的步数 */
    }
    else /* 步数不足以进行足够的加减速 */
    {
        /* 步数不足不足以运动,要把前面申请的速度表所占内存释放,以便后续可重复申请 */
        free(g_calc_t.accel_tab);      /* 释放加速段速度表 */
        free(g_calc_t.decel_tab);      /* 释放减速段速度表 */
        printf("\r\nPARAM ERROR");
        return;
    }

    g_calc_t.step_pos = 0;
    g_motor_sta = STATE_ACCEL;            /* 电机为加速状态 */

    g_calc_t.ptr    = g_calc_t.accel_tab; /* 把加速段的速度表存储到ptr里边 */
    g_toggle_pulse  = (uint32_t)(T1_FREQ / (*g_calc_t.ptr));
    g_calc_t.ptr++;

    TIM1->CNT  = 0;
    TIM1->CCR1 = (uint16_t)(g_toggle_pulse / 2);     /*  设置定时器比较值 */

    TIM_CCxCmd(TIM1, TIM_CHANNEL_1, TIM_CCx_Enable); /* 使能定时器通道 */
}

 

3.5.定时器比较中断实现

void TIM1_CC_IRQHandler(void)
{
    volatile uint32_t Tim_Count = 0;
    volatile uint32_t tmp       = 0;
    volatile float Tim_Pulse    = 0;
    volatile static uint8_t i   = 0;

    if (TIM_GetFlagStatus(TIM1, TIM_IT_CC1) != RESET)
    {
        TIM_ClearFlag(TIM1, TIM_IT_CC1);

        i++;                           /* 定时器中断次数计数值 */

        if (i == 2)                    /* 2次,说明已经输出一个完整脉冲 */
        {
            i = 0;                     /* 清零定时器中断次数计数值 */
            g_step_pos++;              /* 当前位置 */

            if ((g_motor_sta != STATE_IDLE) && (g_motor_sta != STATE_STOP))
            {
                g_calc_t.step_pos++;
            }

            switch (g_motor_sta)
            {
                case STATE_ACCEL:
                    g_add_pulse_count++;
                    Tim_Pulse = T1_FREQ / (*g_calc_t.ptr);        /* 由速度表得到每一步的定时器计数值 */
                    g_calc_t.ptr++;                               /* 取速度表的下一位 */
                    g_toggle_pulse = (uint16_t)(Tim_Pulse / 2);   /* 翻转模式C需要除以2 */

                    if (g_calc_t.step_pos >= g_calc_t.accel_step) /* 当大于加速段步数就进入匀速 */
                    {
                        free(g_calc_t.accel_tab);                 /* 运动完要释放内存 */
                        g_motor_sta = STATE_AVESPEED;
                    }

                    break;

                case STATE_DECEL:
                    g_add_pulse_count++;

                    Tim_Pulse = T1_FREQ / (*g_calc_t.ptr); /* 由速度表得到每一步的定时器计数值 */
                    g_calc_t.ptr++;
                    g_toggle_pulse = (uint16_t)(Tim_Pulse / 2);

                    if (g_calc_t.step_pos >= g_calc_t.step)
                    {
                        free(g_calc_t.decel_tab); /* 运动完要释放内存 */
                        g_motor_sta = STATE_STOP;
                    }
                    break;

                case STATE_AVESPEED:
                    g_add_pulse_count++;

                    Tim_Pulse      = T1_FREQ / g_calc_t.vt;
                    g_toggle_pulse = (uint16_t)(Tim_Pulse / 2);

                    if (g_calc_t.step_pos >= g_calc_t.dec_point)
                    {
                        g_calc_t.ptr = g_calc_t.decel_tab; /* 将减速段的速度表赋值给ptr */
                        g_motor_sta  = STATE_DECEL;
                    }
                    break;

                case STATE_STOP:
                    TIM_CCxCmd(TIM1, TIM_CHANNEL_1, TIM_CCx_Disable); /* 开启对应PWM通道 */
                    g_motor_sta = STATE_IDLE;
                    break;

                case STATE_IDLE:
                    break;

                default:
                    break;
            }
        }

        Tim_Count = TIM1->CCR1;
        tmp = 0xFFFF & (Tim_Count + g_toggle_pulse);
        TIM1->CCR1 = tmp;
    }
}

 

4.演示效果

DEMO

 

5.附件

Project_S.zip (1.61 MB, 下载次数: 0)

最新回复

感谢大佬的精彩分享,这pwm图确实给我学习了一下加减速的的原理。  详情 回复 发表于 昨天 07:04

赞赏

1

查看全部赞赏

个人签名We are a team and we work as a team !

回复
举报

7588

帖子

2

TA的资源

版主

看着效果不错,控制电机的时候可以参加一下大佬的代码


回复

7258

帖子

11

TA的资源

版主

感谢大佬的精彩分享,这pwm图确实给我学习了一下加减速的的原理。

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
推荐帖子
Proteus经典例子

Proteus经典例子软件等级: ★★★ CadenceIC设计(全FPGA-CPLD开发简明教vhdl语言例程集锦.PROTELDXP培训教材proteldxp ...

基于脉冲频率调制的光电测量传输电路的设计

基于脉冲频率调制的光电测量传输电路的设计

家庭电子 教您一招

1.电热水器应勤换镁棒 市售在1000元以下的电热水器的内胆一般采用镀锌铁板卷制而成,其焊缝与管 接头部分虽经涂敷处理,但在长 ...

涛行便携式MP3开发板及MP3语音播放模块资料大放送

涛行便携式MP3开发板是一款为便携功能度身定制的MP3专用学习板,可用3-4节电池供电,整机仅为名片大小.浓缩了涛行电子多年MP3相关 ...

旋转编码器解码

随着电子技术的发展,旋转编码器的应用越来越广泛。下面我们谈谈对旋转编码器的解码。 旋转编码器是用来测量转速的装置 ...

工程师遇到电子技术难题怎么办?MPSNow让难题无所遁形

工程师们,常常以攻克电子技术难题为己任,遇到问题,不解决完,绝不罢休! 但是,英雄也有辛酸泪,遇到疑难杂症,往往求 ...

大佬们,求教一下什么是DFX设计呀

什么是DFX设计

【行空板 Python编程学习主控板】八:试用总结

本帖最后由 数码小叶 于 2022-12-13 22:17 编辑 通过这几周的行空板试用,虽然没有体验完行空板的全部功能,只是选择了几方面 ...

技术人容易陷入哪些误区?

本帖最后由 Nubility 于 2024-6-14 23:22 编辑 技术人最容易陷入哪些误区? 1. 认为做产品赚钱一定是需要很高的技术 ...

【 AI挑战营(进阶)】5.模型部署 1 rknn推理测试

本帖最后由 iexplore123 于 2025-1-12 04:26 编辑 【AI挑战营(进阶)】在RV1106部署InsightFace算法的多人实时人脸识别实战5 ...

关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网 5

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表