嵌入式软件架构设计中的状态机操作

嵌入式电子 2023-09-16 08:47

关注公众号,加星标,回复1024获取学习资料,每天进步一点点。


状态机基本术语




  • 现态:是指当前所处的状态。

  • 条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。

  • 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。

  • 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

 

传统有限状态机Fsm实现方法



如图,是一个定时计数器,计数器存在两种状态,一种为设置状态,一种为计时状态。


设置状态

  • “+” “-” 按键对初始倒计时进行设置
  • 当计数值设置完成,点击确认键启动计时 ,即切换到计时状态


计时状态

  • 按下“+” “-” 会进行密码的输入“+”表示1 ,“-”表示输入0 ,密码共有4位
  • 确认键:只有输入的密码等于默认密码,按确认键才能停止计时,否则计时直接到零,并执行相关操作

 

嵌套switch


/***************************************
1.列出所有的状态
***************************************/

typedef enum{
    SETTING,
    TIMING
} STATE_TYPE;

/***************************************
2.列出所有的事件
***************************************/

typedef enum{
    UP_EVT,
    DOWN_EVT,
    ARM_EVT,
    TICK_EVT
} EVENT_TYPE;

/***************************************
3.定义和状态机相关结构
***************************************/

struct  bomb
{
    uint8_t state;
    uint8_t timeout;
    uint8_t code;
    uint8_t defuse_code;
} bomb1;

/***************************************
4.初始化状态机
***************************************/

void bomb1_init(void)
{
    bomb1.state = SETTING;
    bomb1.defuse_code = 6;    //0110 
}

/***************************************
5. 状态机事件派发
***************************************/

void bomb1_fsm_dispatch(EVENT_TYPE evt ,void* param)
{
    switch(bomb1.state)
    {
        case SETTING:
        {
            switch(evt)
            {
                case UP_EVT:    // "+"   按键按下事件
                    if(bomb1.timeout< 60)  
                        ++bomb1.timeout;
                    bsp_display(bomb1.timeout);
                break;
                
                case DOWN_EVT:  // "-"   按键按下事件
                    if(bomb1.timeout > 0)  
                        --bomb1.timeout;
                    bsp_display(bomb1.timeout);
                break;
                
                case ARM_EVT:   // "确认" 按键按下事件
                    bomb1.state = TIMING;
                    bomb1.code  = 0;
                break;
            }
        } 
        break
        
        case TIMING:
        {
            switch(evt)
            {
                case UP_EVT:     // "+"   按键按下事件
                    bomb1.code = (bomb1.code <<1) | 0x01;
                break;
                
                case DOWN_EVT:  // "-"   按键按下事件
                    bomb1.code = (bomb1.code <<1); 
                break;
                
                case ARM_EVT:   // "确认" 按键按下事件
                    if(bomb1.code == bomb1.defuse_code)
                    {
                        bomb1.state = SETTING;
                    }
                    else
                    {
                        bsp_display("bomb!")
                    }
                break;
                
                case TICK_EVT:
                    if(bomb1.timeout)
                    {
                        --bomb1.timeout;
                        bsp_display(bomb1.timeout);
                    }
                    if(bomb1.timeout == 0)
                    {
                        bsp_display("bomb!")
                    }
                break;      
            }   
        }
        break;
    }
}

优点

简单,代码阅读连贯,容易理解

缺点

  • 当状态或事件增多时,代码状态函数需要经常改动,状态事件处理函数会代码量会不断增加

  • 状态机没有进行封装,移植性差。

  • 没有实现状态的进入和退出的操作。进入和退出在状态机中尤为重要:

  • 进入事件:只会在刚进入时触发一次,主要作用是对状态进行必要的初始化

  • 退出事件:只会在状态切换时触发一次 ,主要的作用是清除状态产生的中间参数,为下次进入提供干净环境

 

状态表


二维状态转换表
状态机可以分为状态和事件 ,状态的跃迁都是受事件驱动的,因此可以通过一个二维表格来表示状态的跃迁。

(*) 仅当( code == defuse_code) 时才发生到 setting 的转换。

/*1.列出所有的状态*/
enum
{
    SETTING,
    TIMING,
    MAX_STATE
};

/*2.列出所有的事件*/
enum
{
    UP_EVT,
    DOWN_EVT,
    ARM_EVT,
    TICK_EVT,
    MAX_EVT
};
      
/*3.定义状态表*/
typedef void (*fp_state)(EVT_TYPE evt , void* param);
static  const fp_state  bomb2_table[MAX_STATE][MAX_EVENT] =
{
    {setting_UP, setting_DOWN, setting_ARM, null},
    {setting_UP, setting_DOWN, setting_ARM, timing_TICK}
};
      
struct bomb_t
{
    const fp_state const *state_table;  /* the State-Table */
    uint8_t state;  /* the current active state */
          
    uint8_t timeout;
    uint8_t code;
    uint8_t defuse_code;
};

struct bomb bomb2=
{
    .state_table = bomb2_table;
}

void bomb2_init(void)
{
    bomb2.defuse_code = 6// 0110
    bomb2.state = SETTING;
}
      
void bomb2_dispatch(EVT_TYPE evt , void* param)
{
    fp_state  s = NULL;
    if(evt > MAX_EVT)
    {
        LOG("EVT type error!");
        return;
    }
    s = bomb2.state_table[bomb2.state * MAX_EVT + evt];
    if(s != NULL)
    {
        s(evt , param);
    }
}

/*列出所有的状态对应的事件处理函数*/
void setting_UP(EVT_TYPE evt, void* param)
{
    if(bomb1.timeout< 60)  
        ++bomb1.timeout;
    bsp_display(bomb1.timeout);
}


  • 优点
    • 各个状态面向用户相对独立,增加事件和状态不需要去修改先前已存在的状态事件函数。
    • 可将状态机进行封装,有较好的移植性
函数指针的安全转换 , 利用下面的特性,用户可以扩展带有私有属性的状态机和事件而使用统一的基础状态机接口

typedef void (*Tran)(struct StateTableTag *me, Event const *e);

void Bomb2_setting_ARM (Bomb2 *me, Event const *e);


typedef struct Bomb
{
    struct StateTableTag *me;  //必须为第一个成员
    uint8_t private;
}


  • 缺点

  • 函数粒度太小是最明显的一个缺点,一个状态和一个事件就会产生一个函数,当状态和事件较多时,处理函数将增加很快,在阅读代码时,逻辑分散。

  • 没有实现进入退出动作。

一维状态转换表

实现原理:

typedef void (*fp_action)(EVT_TYPE evt,void* param);
    
/*转换表基础结构*/
struct tran_evt_t
{
    EVT_TYPE evt;
    uint8_t next_state;
};

/*状态的描述*/
struct  fsm_state_t
{
    fp_action  enter_action;  // 进入动作
    fp_action  exit_action;   // 退出动作
    fp_action  action;           
        
    tran_evt_t* tran;    // 转换表
    uint8_t     tran_nb; // 转换表的大小
    const char* name;
}

/*状态表本体*/
#define  ARRAY(x)   x,sizeof(x)/sizeof(x[0])
const struct fsm_state_t state_table[]=
{
    {setting_enter, setting_exit, setting_action, ARRAY(set_tran_evt), "setting" },
    {timing_enter, timing_exit, timing_action, ARRAY(time_tran_evt), "timing" }
};
    
/*构建一个状态机*/
struct fsm
{
    const struct state_t * state_table/* the State-Table */
    uint8_t cur_state;     /* the current active state */
        
    uint8_t timeout;
    uint8_t code;
    uint8_t defuse_code;
} bomb3;
    
/*初始化状态机*/
void bomb3_init(void)
{
    bomb3.state_table = state_table;  // 指向状态表
    bomb3.cur_state = setting;
    bomb3.defuse_code = 8;  //1000
}

/*状态机事件派发*/
void  fsm_dispatch(EVT_TYPE evt, void* param)
{
    tran_evt_t* p_tran = NULL;
        
    /*获取当前状态的转换表*/
    p_tran = bomb3.state_table[bomb3.cur_state]->tran;
        
    /*判断所有可能的转换是否与当前触发的事件匹配*/
    for(uint8_t i=0; i    {
        if(p_tran[i]->evt == evt)  // 事件会触发转换
        {
            if(NULL != bomb3.state_table[bomb3.cur_state].exit_action)
            {
                bomb3.state_table[bomb3.cur_state].exit_action(NULL);  // 执行退出动作
            }
            if(bomb3.state_table[_tran[i]->next_state].enter_action)
            {
                bomb3.state_table[_tran[i]->next_state].enter_action(NULL);  // 执行进入动作
            }
            
            /*更新当前状态*/
            bomb3.cur_state = p_tran[i]->next_state;
        }
        else
        {
            bomb3.state_table[bomb3.cur_state].action(evt, param);
        }
    }
}

/*************************************************************************
setting状态相关
************************************************************************/

void setting_enter(EVT_TYPE evt, void* param)
{
        
}
void setting_exit(EVT_TYPE evt, void* param)
{
        
}
void setting_action(EVT_TYPE evt, void* param)
{
        
}
tran_evt_t set_tran_evt[] =
{
    {ARM , timing},
}
/*timing 状态相关*/


  • 优点
    • 各个状态面向用户相对独立,增加事件和状态不需要去修改先前已存在的状态事件函数。
    • 实现了状态的进入和退出
    • 容易根据状态跃迁图来设计 (状态跃迁图列出了每个状态的跃迁可能,也就是这里的转换表)
    • 实现灵活,可实现复杂逻辑,如上一次状态,增加监护条件来减少事件的数量。可实现非完全事件驱动
  • 缺点

    • 函数粒度较小(比二维小且增长慢),可以看到,每一个状态需要至少3个函数,还需要列出所有的转换关系。

 

QP嵌入式实时框架


特点

  • 事件驱动型编程
    • 好莱坞原则:和传统的顺序式编程方法例如“超级循环”,或传统的RTOS 的任务不同。绝大多数的现代事件驱动型系统根据好莱坞原则被构造,(Don’t call me; I’ll call you.)

  • 面向对象
    • 类和单一继承

  • 工具
    • QM :一个通过UML类图来描述状态机的软件,并且可以自动生成C代码

    • QS软件追踪工具



 

QEP 实现有限状态机 Fsm

  • 实现

/* qevent.h ----------------------------------------------------------------*/
typedef struct QEventTag 
{  
    QSignal sig;     
    uint8_t dynamic_;  
} QEvent;

/* qep.h -------------------------------------------------------------------*/
typedef uint8_t QState;  /* status returned from a state-handler function */
typedef QState (*QStateHandler) (void *me, QEvent const *e); /* argument list */
typedef struct QFsmTag  /* Finite State Machine */

    QStateHandler state;  /* current active state */
} QFsm;
      
#define QFsm_ctor(me_, initial_) ((me_)->state = (initial_))
void QFsm_init (QFsm *me, QEvent const *e);
void QFsm_dispatch(QFsm *me, QEvent const *e);
      
#define Q_RET_HANDLED ((QState)0)
#define Q_RET_IGNORED ((QState)1)
#define Q_RET_TRAN    ((QState)2)
#define Q_HANDLED()   (Q_RET_HANDLED)
#define Q_IGNORED()   (Q_RET_IGNORED)
      
#define Q_TRAN(target_) (((QFsm *)me)->state = (QStateHandler)   (target_),Q_RET_TRAN)
      
enum QReservedSignals
{
    Q_ENTRY_SIG = 1
    Q_EXIT_SIG, 
    Q_INIT_SIG, 
    Q_USER_SIG 
};
      
/* file qfsm_ini.c ---------------------------------------------------------*/
#include "qep_port.h"  /* the port of the QEP event processor */
#include "qassert.h"  /* embedded systems-friendly assertions */
void QFsm_init(QFsm *me, QEvent const *e) 
{
    (*me->state)(me, e);  /* execute the top-most initial transition */
    /* enter the target */
    (void)(*me->state)(me , &QEP_reservedEvt_[Q_ENTRY_SIG]);
}

/* file qfsm_dis.c ---------------------------------------------------------*/
void QFsm_dispatch(QFsm *me, QEvent const *e)
{
    QStateHandler s = me->state;  /* save the current state */
    QState r = (*s)(me, e);  /* call the event handler */
    if (r == Q_RET_TRAN)  /* transition taken? */
    {
        (void)(*s)(me, &QEP_reservedEvt_[Q_EXIT_SIG]); /* exit the source */
        (void)(*me->state)(me, &QEP_reservedEvt_[Q_ENTRY_SIG]);/*enter target*/
    }
}

// 实现上面定时器例子
#include "qep_port.h" /* the port of the QEP event processor */
#include "bsp.h"      /* board support package */
      
enum BombSignals  /* all signals for the Bomb FSM */

    UP_SIG = Q_USER_SIG,
    DOWN_SIG,
    ARM_SIG,
    TICK_SIG
};

typedef struct TickEvtTag 
{
    QEvent super;      /* derive from the QEvent structure */
    uint8_t fine_time; /* the fine 1/10 s counter */
} TickEvt;
      
typedef struct Bomb4Tag 
{
    QFsm super;      /* derive from QFsm */
    uint8_t timeout; /* number of seconds till explosion */
    uint8_t code;    /* currently entered code to disarm the bomb */
    uint8_t defuse;  /* secret defuse code to disarm the bomb */
} Bomb4;
      
void Bomb4_ctor (Bomb4 *me, uint8_t defuse);
QState Bomb4_initial(Bomb4 *me, QEvent const *e);
QState Bomb4_setting(Bomb4 *me, QEvent const *e);
QState Bomb4_timing (Bomb4 *me, QEvent const *e);
/*--------------------------------------------------------------------------*/
/* the initial value of the timeout */
#define INIT_TIMEOUT 10
/*..........................................................................*/
void Bomb4_ctor(Bomb4 *me, uint8_t defuse) {
    QFsm_ctor_(&me->super, (QStateHandler)&Bomb4_initial);
    me->defuse = defuse;  /* the defuse code is assigned at instantiation */
}
/*..........................................................................*/
QState Bomb4_initial(Bomb4 *me, QEvent const *e) 
{
    (void)e;
    me->timeout = INIT_TIMEOUT;
    return Q_TRAN(&Bomb4_setting);
}
/*..........................................................................*/
QState Bomb4_setting(Bomb4 *me, QEvent const *e) 
{
    switch (e->sig)
    {
        case UP_SIG:
        {
            if (me->timeout < 60
            {
                ++me->timeout;
                BSP_display(me->timeout);
            }
            return Q_HANDLED();
        }
        
        case DOWN_SIG: 
        {
            if (me->timeout > 1
            {
                --me->timeout;
                BSP_display(me->timeout);
            }
            return Q_HANDLED();
        }

        case ARM_SIG: 
        {
            return Q_TRAN(&Bomb4_timing); /* transition to "timing" */
        }
    }
    return Q_IGNORED();
}
      
/*..........................................................................*/
void Bomb4_timing(Bomb4 *me, QEvent const *e) 
{
    switch (e->sig) 
    {
        case Q_ENTRY_SIG: 
        {
            me->code = 0/* clear the defuse code */
            return Q_HANDLED();
        }
        
        case UP_SIG: 
        {
            me->code <<= 1;
            me->code |= 1;
            return Q_HANDLED();
        }
        
        case DOWN_SIG: 
        {
            me->code <<= 1;
            return Q_HANDLED();
        }
        
        case ARM_SIG: 
        {
            if (me->code == me->defuse) 
            {
                return Q_TRAN(&Bomb4_setting);
            }
            return Q_HANDLED();
        }
        
        case TICK_SIG: 
        {
            if (((TickEvt const *)e)->fine_time == 0
            {
                --me->timeout;
                BSP_display(me->timeout);
                if (me->timeout == 0
                {
                    BSP_boom(); /* destroy the bomb */
                }
            }
            return Q_HANDLED();
        }
    }
    return Q_IGNORED();
}


  • 优点

    • 采用面向对象的设计方法,很好的移植性

    • 实现了进入退出动作

    • 合适的粒度,且事件的粒度可控

    • 状态切换时通过改变指针,效率高

    • 可扩展成为层次状态机

  • 缺点
    • 对事件的定义以及事件粒度的控制是设计的最大难点,如串口接收到一帧数据,这些变量的更新单独作为某个事件,还是串口收到数据作为一个事件。再或者显示屏,如果使用此种编程方式,如何设计事件。

 

QP 实现层次状态机 Hsm简介


初始化:

初始化层次状态机的实现:在初始化时,用户所选取的状态永远是最底层的状态,如上图,我们在计算器开机后,应该进入的是开始状态。
这就涉及到一个问题,由最初top(顶状态)到begin 是有一条状态切换路径的,当我们设置状态为 begin,如何搜索这条路径成为关键(知道了路径才能正确的进入begin,要执行路径中过渡状态的进入和退出事件)
void QHsm_init(QHsm *me, QEvent const *e) 
{
    Q_ALLEGE((*me->state)(me, e) == Q_RET_TRAN);
    t = (QStateHandler)&QHsm_top; /* HSM starts in the top state */
    do 
    {  /* drill into the target... */
        QStateHandler path[QEP_MAX_NEST_DEPTH_];
        int8_t ip = (int8_t)0/* transition entry path index */
        path[0] = me->state; /* 这里的状态为begin */
            
        /*通过执行空信号,从底层状态找到顶状态的路径*/
        (void)QEP_TRIG_(me->state, QEP_EMPTY_SIG_);
        while (me->state != t) 
        {
            path[++ip] = me->state;
            (void)QEP_TRIG_(me->state, QEP_EMPTY_SIG_);
        }
        /*切换为begin*/
        me->state = path[0]; /* restore the target of the initial tran. */
        /* 钻到最底层的状态,执行路径中的所有进入事件 */
        Q_ASSERT(ip < (int8_t)QEP_MAX_NEST_DEPTH_);
        do 
        {  /* retrace the entry path in reverse (desired) order... */
            QEP_ENTER_(path[ip]); /* enter path[ip] */
        } 
        while ((--ip) >= (int8_t)0);
            
        t = path[0]; /* current state becomes the new source */
    } 
    while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN);
    me->state = t;
}

t = path[0]; /* target of the transition */
if (s == t) 
{  /* (a) check source==target (transition to self) */
    QEP_EXIT_(s) /* exit the source */
    ip = (int8_t)0/* enter the target */
}
else 
{
    (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); /* superstate of target */
    t = me->state;
    if (s == t) 
    {  /* (b) check source==target->super */
        ip = (int8_t)0/* enter the target */
    }
    else 
    {
        (void)QEP_TRIG_(s, QEP_EMPTY_SIG_); /* superstate of src */
        /* (c) check source->super==target->super */
        if(me->state == t) 
        {
            QEP_EXIT_(s) /* exit the source */
            ip = (int8_t)0/* enter the target */
        }
        else 
        {
            /* (d) check source->super==target */
            if (me->state == path[0]) 
            {
                QEP_EXIT_(s) /* exit the source */
            }
            else 
            {  /* (e) check rest of source==target->super->super..
                * and store the entry path along the way */

                 ....


 

QP实时框架的组成


内存管理

使用内存池,对于低性能mcu,内存极为有限,引入内存管理主要是整个架构中,是以事件作为主要的任务通信手段,且事件是带参数的。可能相同类型的事件会多次触发,而事件处理完成后,需要清除事件,无法使用静态的事件,因此是有必要为不同事件创建内存池的。

对于不同块大小的内存池,需要考虑的是每个块的起始地址对齐问题。在进行内存池初始化时,我们是根据 blocksize+header 大小来进行划分内存池的。假设一个 2 字节的结构,如果以 2 来进行划分,假设 mcu 4 字节对齐,那么将有一半的结构起始地址无法对齐,这时需要为每个块预留空间,保证每个块的对齐。


事件队列
  • 每一个活动对象维护一个事件队列,事件都是由基础事件派生的,不同类型的事件只需要将其基础事件成员添加到活动对象的队列中即可,最终在取出的时候通过一个强制转换便能获得附加的参数。


事件派发

  • 直接事件发送
    • QActive_postLIFO()
  • 发行订阅事件发送
    • 竖轴表示信号(为事件的基类)
    • 活动对象支持64个优先级,每一个活动对象要求拥有唯一优先级
    • 通过优先级的bit位来表示某个事件被哪些活动对象订阅,并在事件触发后根据优先级为活动对象派发事件。

定时事件
  • 非有序链表

  • 合作式调度器QV


  • 可抢占式调度器QK

 

QP nano 的简介


  • 完全支持层次式状态嵌套,包括在最多4 层状态嵌套情况下,对任何状态转换拓扑的可保证的进入/ 退出动作

  • 支持高达8 个并发执行的,可确定的,线程安全的事件队列的活动对象57

  • 支持一个字节宽( 255 个信号)的信号,和一个可伸缩的参数,它可被配置成0 (没有参数), 1 , 2 或4 字节

  • 使用先进先出FIFO排队策略的直接事件派发机制

  • 每个活动对象有一个一次性时间事件(定时器),它的可配置动态范围是0(没有时间事件) , 1 , 2 或4 字节

  • 内建的合作式 vanilla 内核

  • 内建的名为 QK-nano 的可抢占型 RTC 内核

  • 带有空闲回调函数的低功耗架构,用来方便的实现节省功耗模式。

  • 在代码里为流行的低端CPU架构的C编译器的非标准扩展进行了准备(例如,在代码空间分配常数对象,可重入函数,等等)

  • 基于断言的错误处理策略

  • 代码风格






原文:http://t.csdn.cn/VZC0X

文章来源于网络,版权归原作者所有,如有侵权,请联系删除。


关注公众号,加星标,回复1024获取学习资料,每天进步一点点。


声明:

本号原创、转载的文章、图片等版权归原作者所有,如有侵权,请联系删除。

关注、点赞、在看、转发,支持优质内容! 

评论
  • 《高速PCB设计经验规则应用实践》+PCB绘制学习与验证读书首先看目录,我感兴趣的是这一节;作者在书中列举了一条经典规则,然后进行详细分析,通过公式推导图表列举说明了传统的这一规则是受到电容加工特点影响的,在使用了MLCC陶瓷电容后这一条规则已经不再实用了。图书还列举了高速PCB设计需要的专业工具和仿真软件,当然由于篇幅所限,只是介绍了一点点设计步骤;我最感兴趣的部分还是元件布局的经验规则,在这里列举如下:在这里,演示一下,我根据书本知识进行电机驱动的布局:这也算知行合一吧。对于布局书中有一句:
    wuyu2009 2024-11-30 20:30 86浏览
  • 国产光耦合器正以其创新性和多样性引领行业发展。凭借强大的研发能力,国内制造商推出了适应汽车、电信等领域独特需求的专业化光耦合器,为各行业的技术进步提供了重要支持。本文将重点探讨国产光耦合器的技术创新与产品多样性,以及它们在推动产业升级中的重要作用。国产光耦合器创新的作用满足现代需求的创新模式新设计正在满足不断变化的市场需求。例如,高速光耦合器满足了电信和数据处理系统中快速信号传输的需求。同时,栅极驱动光耦合器支持电动汽车(EV)和工业电机驱动器等大功率应用中的精确高效控制。先进材料和设计将碳化硅
    克里雅半导体科技 2024-11-29 16:18 157浏览
  • 光伏逆变器是一种高效的能量转换设备,它能够将光伏太阳能板(PV)产生的不稳定的直流电压转换成与市电频率同步的交流电。这种转换后的电能不仅可以回馈至商用输电网络,还能供独立电网系统使用。光伏逆变器在商业光伏储能电站和家庭独立储能系统等应用领域中得到了广泛的应用。光耦合器,以其高速信号传输、出色的共模抑制比以及单向信号传输和光电隔离的特性,在光伏逆变器中扮演着至关重要的角色。它确保了系统的安全隔离、干扰的有效隔离以及通信信号的精准传输。光耦合器的使用不仅提高了系统的稳定性和安全性,而且由于其低功耗的
    晶台光耦 2024-12-02 10:40 54浏览
  • 戴上XR眼镜去“追龙”是种什么体验?2024年11月30日,由上海自然博物馆(上海科技馆分馆)与三湘印象联合出品、三湘印象旗下观印象艺术发展有限公司(下简称“观印象”)承制的《又见恐龙》XR嘉年华在上海自然博物馆重磅开幕。该体验项目将于12月1日正式对公众开放,持续至2025年3月30日。双向奔赴,恐龙IP撞上元宇宙不久前,上海市经济和信息化委员会等部门联合印发了《上海市超高清视听产业发展行动方案》,特别提到“支持博物馆、主题乐园等场所推动超高清视听技术应用,丰富线下文旅消费体验”。作为上海自然
    电子与消费 2024-11-30 22:03 70浏览
  • 在现代科技浪潮中,精准定位技术已成为推动众多关键领域前进的核心力量。虹科PCAN-GPS FD 作为一款多功能可编程传感器模块,专为精确捕捉位置和方向而设计。该模块集成了先进的卫星接收器、磁场传感器、加速计和陀螺仪,能够通过 CAN/CAN FD 总线实时传输采样数据,并具备内部存储卡记录功能。本篇文章带你深入虹科PCAN-GPS FD的技术亮点、多场景应用实例,并展示其如何与PCAN-Explorer6软件结合,实现数据解析与可视化。虹科PCAN-GPS FD虹科PCAN-GPS FD的数据处
    虹科汽车智能互联 2024-11-29 14:35 149浏览
  • 最近几年,新能源汽车愈发受到消费者的青睐,其销量也是一路走高。据中汽协公布的数据显示,2024年10月,新能源汽车产销分别完成146.3万辆和143万辆,同比分别增长48%和49.6%。而结合各家新能源车企所公布的销量数据来看,比亚迪再度夺得了销冠宝座,其10月新能源汽车销量达到了502657辆,同比增长66.53%。众所周知,比亚迪是新能源汽车领域的重要参与者,其一举一动向来为外界所关注。日前,比亚迪汽车旗下品牌方程豹汽车推出了新车方程豹豹8,该款车型一上市就迅速吸引了消费者的目光,成为SUV
    刘旷 2024-12-02 09:32 59浏览
  • 艾迈斯欧司朗全新“样片申请”小程序,逾160种LED、传感器、多芯片组合等产品样片一触即达。轻松3步完成申请,境内免费包邮到家!本期热荐性能显著提升的OSLON® Optimal,GF CSSRML.24ams OSRAM 基于最新芯片技术推出全新LED产品OSLON® Optimal系列,实现了显著的性能升级。该系列提供五种不同颜色的光源选项,包括Hyper Red(660 nm,PDN)、Red(640 nm)、Deep Blue(450 nm,PDN)、Far Red(730 nm)及Ho
    艾迈斯欧司朗 2024-11-29 16:55 155浏览
  • 学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&
    youyeye 2024-11-30 14:30 63浏览
  • 在电子技术快速发展的今天,KLV15002光耦固态继电器以高性能和强可靠性完美解决行业需求。该光继电器旨在提供无与伦比的电气隔离和无缝切换,是现代系统的终极选择。无论是在电信、工业自动化还是测试环境中,KLV15002光耦合器固态继电器都完美融合了效率和耐用性,可满足当今苛刻的应用需求。为什么选择KLV15002光耦合器固态继电器?不妥协的电压隔离从本质上讲,KLV15002优先考虑安全性。输入到输出隔离达到3750Vrms(后缀为V的型号为5000Vrms),确保即使在高压情况下,敏感的低功耗
    克里雅半导体科技 2024-11-29 16:15 119浏览
  • RDDI-DAP错误通常与调试接口相关,特别是在使用CMSIS-DAP协议进行嵌入式系统开发时。以下是一些可能的原因和解决方法: 1. 硬件连接问题:     检查调试器(如ST-Link)与目标板之间的连接是否牢固。     确保所有必要的引脚都已正确连接,没有松动或短路。 2. 电源问题:     确保目标板和调试器都有足够的电源供应。     检查电源电压是否符合目标板的规格要求。 3. 固件问题: &n
    丙丁先生 2024-12-01 17:37 57浏览
  • By Toradex胡珊逢简介嵌入式领域的部分应用对安全、可靠、实时性有切实的需求,在诸多实现该需求的方案中,QNX 是经行业验证的选择。在 QNX SDP 8.0 上 BlackBerry 推出了 QNX Everywhere 项目,个人用户可以出于非商业目的免费使用 QNX 操作系统。得益于 Toradex 和 QNX 的良好合作伙伴关系,用户能够在 Apalis iMX8QM 和 Verdin iMX8MP 模块上轻松测试和评估 QNX 8 系统。下面将基于 Apalis iMX8QM 介
    hai.qin_651820742 2024-11-29 15:29 150浏览
  • 国产光耦合器因其在电子系统中的重要作用而受到认可,可提供可靠的电气隔离并保护敏感电路免受高压干扰。然而,随着行业向5G和高频数据传输等高速应用迈进,对其性能和寿命的担忧已成为焦点。本文深入探讨了国产光耦合器在高频环境中面临的挑战,并探索了克服这些限制的创新方法。高频性能:一个持续关注的问题信号传输中的挑战国产光耦合器传统上利用LED和光电晶体管进行信号隔离。虽然这些组件对于标准应用有效,但在高频下面临挑战。随着工作频率的增加,信号延迟和数据保真度降低很常见,限制了它们在电信和高速计算等领域的有效
    腾恩科技-彭工 2024-11-29 16:11 106浏览
  • 光耦合器作为关键技术组件,在确保安全性、可靠性和效率方面发挥着不可或缺的作用。无论是混合动力和电动汽车(HEV),还是军事和航空航天系统,它们都以卓越的性能支持高要求的应用环境,成为现代复杂系统中的隐形功臣。在迈向更环保技术和先进系统的过程中,光耦合器的重要性愈加凸显。1.混合动力和电动汽车中的光耦合器电池管理:保护动力源在电动汽车中,电池管理系统(BMS)是最佳充电、放电和性能监控背后的大脑。光耦合器在这里充当守门人,将高压电池组与敏感的低压电路隔离开来。这不仅可以防止潜在的损坏,还可以提高乘
    腾恩科技-彭工 2024-11-29 16:12 117浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦