嵌入式软件开发中使用设计模式的思想

一起学嵌入式 2023-07-27 07:50

扫描关注一起学嵌入式,一起学习,一起成长


   1、嵌入式     

嵌入式的标签多为:低配,偏硬件,底层,资源紧张,代码多以C语言,汇编为主,代码应用逻辑简单。
但随着AIOT时代的到来,局面组件改变。芯片的性能资源逐渐提升,业务逻辑也逐渐变得复杂,相对于代码的效率而言,代码的复用可移植性要求越来越高,以获得更短的项目周期 和更高的可维护性。下面是AIOT时代嵌入式设备的常见的软件框架。

设计模式

设计模式的标签:高级语言 ,高端,架构等。在AIOT时代,设计模式与嵌入式能擦出怎样的火花?设计模式可描述为:对于某类相似的问题,经过前人的不断尝试,总结出了处理此类问题的公认的有效解决办法。

嵌入式主要以C语言开发,且面向过程,而设计模式常见于高级语言(面向对象),目前市面上描述设计模式的书籍多数使用JAVA 语言,C语言能实现设计模式吗?设计模式与语言无关,它是解决问题的方法,JAVA可以实现,C语言同样可以实现。同样的,JAVA程序员会遇到需要用模式来处理的问题,C程序员也可能遇见,因此设计模式是很有必要学习的。

模式陷阱:设计模式是针对具体的某些类问题的有效解决办法,不是所有的问题都能匹配到对应的设计模式。因此,不能一味的追求设计模式,有时候简单直接的处理反而更有效。有的问题没有合适的模式可以尽量满足一些设计原则,如开闭原则(对扩展开放,对修改关闭)

   2、观察者模式  

在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。

实现

主题对象提供统一的注册接口,以及注册函数 。由观察者本身实例化observer_intf 接口,然后使用注册函数,添加到对应的主题列表中,主题状态发生改变,依次通知列表中的所有对象。
 struct observer_ops
 {
     void*(handle)(uint8_t evt);  
 };
 struct observer_intf
 {
     struct observer_intf* next;
     const char* name;
     void* condition;
     const struct observer_ops *ops;
 }
 int observer_register(struct topical* top , struct observer_intf* observer);

当主题状态发生改变,将通知到所有观察者,观察者本身也可以设置条件,是否选择接收通知。

 struct observer_intf observer_list;
     
 void XXXX_topical_evt(uint8_t evt)
 {
      struct observer_intf* cur_observer = observer_list.next;
      uint8_t* condition = NULL;
      while(cur_observer != NULL)
      {
          condition = (uint8_t*)cur_observer->condition;
          if(NULL == condition || (condition && *condition))
          {
              if(cur_observer->ops->handle){
                  cur_observer->ops->handle(evt);
              }       
          }
          cur_observer = cur_observer->next;
      }
 }
实例:嵌入式裸机低功耗框架
  • 设备功耗分布

其中线路损耗,电源电路等软件无法控制,故不讨论。板载外设,如传感器可能通过某条命令配置进入低功耗模式,又或者硬件上支持控制外设电源来控制功耗。片内外设,及芯片内部的外设,通过卸载相关驱动,关闭时钟配置工作模式来控制功耗。
  • 设备唤醒方式

    当系统某个定时事件到来时,系统被主动唤醒处理事件

    系统处于睡眠,被外部事件唤醒,如串口接收到一包数据,传感器检测到变化,通过引脚通知芯片

    • 被动唤醒
    • 主动唤醒
  • 系统允许睡眠的条件

    • 外设无正在收发的数据

    • 缓存无需要处理的数据

    • 应用层状态处于空闲(无需要处理的事件)

  • 基于观察者模式的PM框架实现

PM组件提供的接口

struct pm
{
    struct pm* next;
    const char* name;
   void(*init)(void);
    void(*deinit(void);
    void* condition;
};
static struct pm pm_list;
static uint8_t pm_num;
static uint8_t pm_status;
         
int pm_register(const struct pm* pm , const char* name)
{
     struct pm* cur_pm =  &pm_list;
     while(cur_pm->next)
     {
         cur_pm = cur_pm->next;
     }
     cur_pm->next = pm;
     pm->next = NULL;
     pm->name = name;
     pm_num++;
}
 
void pm_loop(void)
{
    uint32_t pm_condition = 0;
    struct pm* cur_pm =  pm_list.next;
    static uint8_t cnt;
    
    /*check all condition*/
    while(cur_pm)
    {
        if(cur_pm->condition){
         pm_condition |=  *((uint32_t*)(cur_pm->condition));
        }
        cur_pm = cur_pm->next;
    }
    if(pm_condition == 0)
    {
      cnt++;
        if(cnt>=5)
        {
            pm_status = READY_SLEEP;
        }
    }
    else
    {
        cnt = 0;
    }
    if( pm_status == READY_SLEEP)
    {
         cur_pm =  pm_list.next;
         while(cur_pm)
         {
             if(cur_pm->deinit){
                cur_pm->deinit();
             }
             cur_pm = cur_pm->next;
         }
        pm_status = SLEEP;
        ENTER_SLEEP_MODE();
    }   
    /*sleep--->wakeup*/
    if(pm_status == SLEEP)
    {
         pm_status = NORMAL;
         cur_pm =  pm_list.next;
         while(cur_pm)
         {
             if(cur_pm->init){
                cur_pm->init();
             }
             cur_pm = cur_pm->next;
         }
    }
}

外设使用PM接口

struct uart_dev
{
 ...
 struct pm pm;
    uint32_t pm_condition; 
};
struct uart_dev uart1;
 
void hal_uart1_init(void);
void hal_uart1_deinit(void);
    
void uart_init(void)
{
    uart1.pm.init =  hal_uart1_init;
    uart1.pm.deinit =  hal_uart1_deinit;
    uart1.pm.condition = &uart1.pm_condition;
    pm_register(&uart1.pm , "uart1");
}
/*接下来串口驱动检查缓存 , 发送 , 接收是否空闲或者忙碌 , 给uart1.pm_condition赋值*/
结论
  • PM 电源管理可以单独形成模块,当功耗外设增加时,只需实现接口,注册即可
  • 通过定义段导出操作,可以更加简化应用层或外设的注册逻辑
  • 方便调试,可以很方便打印出系统当前为满足睡眠条件的模块
  • 通过条件字段划分,应该可以实现系统部分睡眠

   3、责任链模式 

在现实生活中,一个事件(任务)需要经过多个对象处理是很常见的场景。如报销流程,公司员工报销, 首先员工整理报销单,核对报销金额,有误则继续核对整理,直到无误,将报销单递交到财务,财务部门进行核对,核对无误后,判断金额数量,若小于一定金额,则财务部门可直接审批,若金额超过范围,则报销单流传到总经理,得到批准后,整个任务才算结束。

类似的情景还有很多,如配置一个WIFI模块,通过AT指令,要想模块正确连入WIFI , 需要按一定的顺序依次发送配置指令 , 如设置设置模式 ,设置 需要连接的WIFI名,密码,每发送一条配置指令,模块都将返回配置结果,而发送者需要判断结果的正确性,再选择是否发送下一条指令或者进行重传。

总结起来是,一系列任务需要严格按照时间线依次处理的顺序逻辑,如下图所示:

在存在系统的情况下,此类逻辑可以很容易的用阻塞延时来实现,实现如下:

void process_task(void)
{
    task1_process();
    msleep(1000);
    
    task2_process();
    mq_recv(¶m , 1000);
    
    task3_process();
    while(mq_recv(¶m , 1000) != OK)
    {
        if(retry)
        {
             task3_process();
             --try;
        }
    }
}

在裸机的情况下,为了保证系统的实时性,无法使用阻塞延时,一般使用定时事件配合状态机来实现:

void process_task(void)
{
     switch(task_state)
     {
         case task1:
             task1_process();
             set_timeout(1000);break;
         case task2:
             task1_process();
             set_timeout(1000);break;
         case task3:
             task1_process();
             set_timeout(1000)break;
         default:break;
     }
}
/*定时器超时回调*/
void timeout_cb(void)
{
    if(task_state == task1)
    {
        task_state = task2;
        process_task();
    }
    else  //task2 and task3
    {
        if(retry)
        {
            retry--;
             process_task();
        }
    }
}
/*任务的应答回调*/
void task_ans_cb(void* param)
{
    if(task==task2)
    {
        task_state = task3;
        process_task();
    }
}

与系统实现相比,裸机的实现更加复杂,为了避免阻塞,只能通过状态和定时器来实现顺序延时的逻辑。可以看到,实现过程相当分散,对于单个任务的处理分散到了3个函数中处理,这样导致的后果是:修改、移植的不便。

而实际的应用中,类似的逻辑相当多,如果按照上面的方法去实现,将会导致应用程序的强耦合。

实现

可以发现,上面的情景有以下特点:

  • 任务按顺序执行,只有当前任务执行完了(有结论,成功或者失败)才允许执行下一个任务
  • 前一个任务的执行结果会影响到下一个任务的执行情况
  • 任务有一些特性,如超时时间,延时时间,重试次数

通过以上信息,我们可以抽象出这样一个模型:任务作为节点; 每一个任务节点有其属性:如超时,延时,重试,参数,处理方法,执行结果。

当需要按照顺序执行一系列任务时,依次将任务节点串成一条链,启动链运行,则从任务链的第一个节点开始运行,运行的结果可以是 OK , BUSY ,ERROR 。

若是OK, 表示节点已处理,从任务链中删除,ERROR 表示运行出错,任务链将停止运行,进行错误回调,可以有用户决定是否继续运行下去。BUSY表示任务链处于等待应答,或者等待延时的情况。当整条任务链上的节点都执行完,进行成功回调。

node数据结构定义

/*shadow node api type for req_chain src*/
typedef struct shadow_resp_chain_node
{
 uint16_t timeout;
 uint16_t duration;
 uint8_t init_retry;
 uint8_t param_type;
 uint16_t retry;
 /*used in mpool*/
   struct shadow_resp_chain_node* mp_prev;
 struct shadow_resp_chain_node* mp_next;
 
    /*used resp_chain*/
 struct shadow_resp_chain_node* next;
 
 node_resp_handle_fp handle;
 void* param;
}shadow_resp_chain_node_t;
node内存池
使用内存池的必要性:实际情况下,同一时间,责任链的条数,以及单条链的节点数比较有限,但种类是相当多的。
比如一个支持AT指令的模块,可能支持几十条AT指令,但执行一个配置操作,可能就只会使用3-5条指令,若全部静态定义节点,将会消耗大量内存资源。因此动态分配是必要的。

初始化node内存池,内存池内所有节点都将添加到free_list。当申请节点时,会取出第一个空闲节点,加入到used_list , 并且接入到责任链。当责任链某一个节点执行完,将会被自动回收(从责任链中删除,并从used_list中删除,然后添加到free_list)

职责链数据结构定义

typedef struct resp_chain
{
   bool enable;           //enble == true 责任链启动 
  bool  is_ans;          //收到应答,与void* param 共同组成应答信号
 
 uint8_t state;            
 const char* name;
 void* param;
 TimerEvent_t timer;
 bool timer_is_running;
 shadow_resp_chain_node_t node;        //节点链
 void(*resp_done)(void* result);       //执行结果回调
}resp_chain_t;

职责链初始化

void resp_chain_init(resp_chain_t* chain ,  const char* name , 
                                            void(*callback)(void* result))
   RESP_ASSERT(chain);
 /*only init one time*/
 resp_chain_mpool_init();
 
   chain->enable = false;
 chain->is_ans = false;
 chain->resp_done = callback;
 chain->name = name;
 
 chain->state = RESP_STATUS_IDLE;
 chain->node.next = NULL;
 chain->param = NULL;
 
 TimerInit(&chain->timer,NULL);
}

职责链添加节点

int resp_chain_node_add(resp_chain_t* chain , 
                        node_resp_handle_fp handle , void* param)
{
   RESP_ASSERT(chain);
 BoardDisableIrq();  
 shadow_resp_chain_node_t* node = chain_node_malloc();
 if(node == NULL)
 {
    BoardEnableIrq();
    RESP_LOG("node malloc error ,no free node");
  return -2;
 }
 /*初始化节点,并加入责任链*/
 shadow_resp_chain_node_t* l = &chain->node;
 while(l->next != NULL)
 {
  l = l->next;
 }
 l->next = node;
 node->next = NULL; 
 node->handle = handle;
 node->param = param;
 node->timeout = RESP_CHIAN_NODE_DEFAULT_TIMEOUT;
 node->duration = RESP_CHIAN_NODE_DEFAULT_DURATION;
 node->init_retry = RESP_CHIAN_NODE_DEFAULT_RETRY;
 node->retry = (node->init_retry == 0)? 0 :(node->init_retry-1);
 BoardEnableIrq();
 return 0;
}

职责链的启动

void resp_chain_start(resp_chain_t* chain)
{
   RESP_ASSERT(chain);
 chain->enable = true;
}

职责链的应答

void resp_chain_set_ans(resp_chain_t* chain , void* param)
{
 RESP_ASSERT(chain);
   if(chain->enable)
 {
  chain->is_ans = true;
  if(param != NULL)
     chain->param = param;
  else
  {
   chain->param = "NO PARAM";
  }
 }
}

职责链的运行

int resp_chain_run(resp_chain_t* chain)

 RESP_ASSERT(chain);
 if(chain->enable)
 {
    shadow_resp_chain_node_t* cur_node = chain->node.next;
    /*maybe ans occur in handle,so cannot change state direct when ans comming*/
    if(chain->is_ans)
  {
   chain->is_ans = false;
   chain->state = RESP_STATUS_ANS;
  }  
   
  switch(chain->state)
  {
   case RESP_STATUS_IDLE:
   {
    if(cur_node)
    {
       uint16_t retry = cur_node->init_retry;
     if(cur_node->handle)
     {
        cur_node->param_type = RESP_PARAM_INPUT;
      chain->state = cur_node->handle((resp_chain_node_t*)cur_node                                                               ,cur_node->param);
     }
     else
     {
         RESP_LOG("node handle is null ,goto next node");
      chain->state = RESP_STATUS_OK;
     }
     if(retry != cur_node->init_retry)
     {
      cur_node->retry = cur_node->init_retry>0?(cur_node-                                                      >init_retry-1):0;                       
     }
    }
    else
    {
       if(chain->resp_done)
     {
      chain->resp_done((void*)RESP_RESULT_OK);
     }
     chain->enable = 0;
     chain->state = RESP_STATUS_IDLE;
     TimerStop(&chain->timer);
     chain->timer_is_running  = false;
    }
    break;
   }
   case RESP_STATUS_DELAY:
   {
    if(chain->timer_is_running == false)
    {
       chain->timer_is_running  = true;
     TimerSetValueStart(&chain->timer , cur_node->duration);
    }
    if(TimerGetFlag(&chain->timer) == true)
    {
     chain->state = RESP_STATUS_OK;
     chain->timer_is_running  = false;
    }
     break
   }
   case RESP_STATUS_BUSY:
   {
      /*waiting for ans or timeout*/
      if(chain->timer_is_running == false)
    {
       chain->timer_is_running  = true;
     TimerSetValueStart(&chain->timer , cur_node->timeout);
    }
    if(TimerGetFlag(&chain->timer) == true)
    {
     chain->state = RESP_STATUS_TIMEOUT;
     chain->timer_is_running  = false;
    }
    break;
      }
   case RESP_STATUS_ANS:
     {
      /*already got the ans,put the param back to the request handle*/
      TimerStop(&chain->timer);
    chain->timer_is_running  = false;
    if(cur_node->handle)
    {
     cur_node->param_type = RESP_PARAM_ANS;
     chain->state = cur_node->handle((resp_chain_node_t*)cur_node ,                                                                 chain->param);
    }
    else
    {
     RESP_LOG("node handle is null ,goto next node");
     chain->state = RESP_STATUS_OK;
    }
    break;
   }
   case RESP_STATUS_TIMEOUT:
   {
    if(cur_node->retry)
    {
     cur_node->retry--; 
     /*retry to request until cnt is 0*/
     chain->state = RESP_STATUS_IDLE;
    }
    else
    {
     chain->state = RESP_STATUS_ERROR;
    }
    break;
   }
   case RESP_STATUS_ERROR:
   {
      if(chain->resp_done)
    {
       chain->resp_done((void*)RESP_RESULT_ERROR);
    }
    chain->enable = 0;
    chain->state = RESP_STATUS_IDLE;
    TimerStop(&chain->timer);
    chain->timer_is_running  = false;
    cur_node->retry = cur_node->init_retry>0?(cur_node->init_retry-1):0;
    chain_node_free_all(chain);
    break;
   }
   case RESP_STATUS_OK:
   {
      /*get the next node*/
      cur_node->retry = cur_node->init_retry>0?(cur_node->init_retry-1):0;
    chain_node_free(cur_node);
    chain->node.next = chain->node.next->next;
    chain->state = RESP_STATUS_IDLE;
    break;
   }
      default:
     break;
  }
 }
 return chain->enable;
}
测试用例
  • 定义并初始化责任链

void chain_test_init(void)
{
    resp_chain_init(&test_req_chain , "test request" , test_req_callback);
}
  • 定义运行函数,在主循环中调用

void chain_test_run(void)
{
    resp_chain_run(&test_req_chain);
}
  • 测试节点添加并启动触发函数
void chain_test_tigger(void)
{
    resp_chain_node_add(&test_req_chain ,  node1_req ,NULL);
    resp_chain_node_add(&test_req_chain ,  node2_req,NULL);
    resp_chain_node_add(&test_req_chain ,  node3_req,NULL);
    resp_chain_start(&test_req_chain);
}
  • 分别实现节点请求函数
 /*延时1s 后执行下一个节点*/
 int node1_req(resp_chain_node_t* cfg, void* param)
 {
     cfg->duration = 1000;
     RESP_LOG("node1 send direct request: delay :%d ms" , cfg->duration);
     return RESP_STATUS_DELAY;
 }
 /*超时时间1S , 重传次数5次*/ 
 int node2_req(resp_chain_node_t* cfg , void* param)
 {
     static uint8_t cnt;
     if(param == NULL)
     {
         cfg->init_retry = 5;
         cfg->timeout  = 1000;
         RESP_LOG("node2 send request max retry:%d , waiting for ans...");
         return RESP_STATUS_BUSY;
     }
     RESP_LOG("node2 get ans: %d",(int)param);
     return RESP_STATUS_OK;
 }
 /*非异步请求*/  
 int node3_req(resp_chain_node_t* cfg , void* param)
 {
     RESP_LOG("node4 send direct request");
     return RESP_STATUS_OK;
 }
 
 void ans_callback(void* param)
 {
     resp_chain_set_ans(&test_req_chain , param);
 }

结论

  • 实现了裸机处理 顺序延时任务
  • 较大程度的简化了应用程的实现,用户只需要实现响应的处理函数 , 调用接口添加,即可按时间要求执行
  • 参数为空,表明为请求 ,否则为应答。(在某些场合,请求可能也带参数,如接下来所说的LAP协议,此时需要通过判断参数的类型)

设计模式参考

Head First 设计模式(中文版)
人人都懂设计模式:从生活中领悟设计模式:Python实现
设计模式之禅
设计模式的C语言应用.https://bbs.huaweicloud.com/blogs/113179
适配器模式

原文:https://blog.csdn.net/qq_36969440/article/details/118189387

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



个人微信开放,扫码添加,进高质量嵌入式交流群


关注我【一起学嵌入式】,一起学习,一起成长。


觉得文章不错,点击“分享”、“”、“在看” 呗!

一起学嵌入式 公众号【一起学嵌入式】,RTOS、Linux编程、C/C++,以及经验分享、行业资讯、物联网等技术知
评论
  • 文/郭楚妤编辑/cc孙聪颖‍相较于一众措辞谨慎、毫无掌舵者个人风格的上市公司财报,利亚德的财报显得尤为另类。利亚德光电集团成立于1995年,是一家以LED显示、液晶显示产品设计、生产、销售及服务为主业的高新技术企业。自2016年年报起,无论业绩优劣,董事长李军每年都会在财报末尾附上一首七言打油诗,抒发其对公司当年业绩的感悟。从“三年翻番顺大势”“智能显示我第一”“披荆斩棘幸从容”等词句中,不难窥见李军的雄心壮志。2012年,利亚德(300296.SZ)在深交所创业板上市。成立以来,该公司在细分领
    华尔街科技眼 2025-05-07 19:25 121浏览
  • 2024年初,OpenAI公布的Sora AI视频生成模型,震撼了国产大模型行业。随后国产厂商集体发力视频大模型,快手发布视频生成大模型可灵,字节跳动发布豆包视频生成模型,正式打响了国内AI视频生成领域第一枪。众多企业匆忙入局,只为在这片新兴市场中抢占先机,却往往忽视了技术成熟度与应用规范的打磨。以社交平台上泛滥的 AI 伪造视频为例,全红婵家人被恶意仿冒博流量卖货,明星们也纷纷中招,刘晓庆、张馨予等均曾反馈有人在视频号上通过AI生成视频假冒她。这些伪造视频不仅严重侵犯他人权
    用户1742991715177 2025-05-05 23:08 79浏览
  • ‌一、高斯计的正确选择‌1、‌明确测量需求‌‌磁场类型‌:区分直流或交流磁场,选择对应仪器(如交流高斯计需支持交变磁场测量)。‌量程范围‌:根据被测磁场强度选择覆盖范围,例如地球磁场(0.3–0.5 G)或工业磁体(数百至数千高斯)。‌精度与分辨率‌:高精度场景(如科研)需选择误差低于1%的仪器,分辨率需匹配微小磁场变化检测需求。2、‌仪器类型选择‌‌手持式‌:便携性强,适合现场快速检测;‌台式‌:精度更高,适用于实验室或工业环境。‌探头类型‌:‌横向/轴向探头‌:根据磁场方向选择,轴向探头适合
    锦正茂科技 2025-05-06 11:36 376浏览
  • 某国产固态电解的2次和3次谐波失真相当好,值得一试。(仅供参考)现在国产固态电解的性能跟上来了,值得一试。当然不是随便搞低端的那种。电容器对音质的影响_电子基础-面包板社区  https://mbb.eet-china.com/forum/topic/150182_1_1.html (右键复制链接打开)电容器对音质的影响相当大。电容器在音频系统中的角色不可忽视,它们能够调整系统增益、提供合适的偏置、抑制电源噪声并隔离直流成分。然而,在便携式设备中,由于空间、成本的限
    bruce小肥羊 2025-05-04 18:14 231浏览
  • 5小时自学修好BIOS卡住问题  更换硬盘故障现象:f2、f12均失效,只有ESC和开关机键可用。错误页面:经过AI的故障截图询问,确定是机体内灰尘太多,和硬盘损坏造成,开机卡在BIOS。经过亲手拆螺丝和壳体、排线,跟换了新的2.5寸硬盘,故障排除。理论依据:以下是针对“5小时自学修好BIOS卡住问题+更换硬盘”的综合性解决方案,结合硬件操作和BIOS设置调整,分步骤说明:一、判断BIOS卡住的原因1. 初步排查     拔掉多余硬件:断开所有外接设备(如
    丙丁先生 2025-05-04 09:14 118浏览
  • 一、gao效冷却与控温机制‌1、‌冷媒流动设计‌采用低压液氮(或液氦)通过毛细管路导入蒸发器,蒸汽喷射至样品腔实现快速冷却,冷却效率高(室温至80K约20分钟,至4.2K约30分钟)。通过控温仪动态调节蒸发器加热功率,结合温度传感器(如PT100铂电阻或Cernox磁场不敏感传感器),实现±0.01K的高精度温度稳定性。2、‌宽温区覆盖与扩展性‌标准温区为80K-325K,通过降压选件可将下限延伸至65K(液氮模式)或4K(液氦模式)。可选配475K高温模块,满足材料在ji端温度下的性能测试需求
    锦正茂科技 2025-04-30 13:08 526浏览
  • 随着智能驾驶时代到来,汽车正转变为移动计算平台。车载AI技术对存储器提出新挑战:既要高性能,又需低功耗和车规级可靠性。贞光科技代理的紫光国芯车规级LPDDR4存储器,以其卓越性能成为国产芯片产业链中的关键一环,为智能汽车提供坚实的"记忆力"支持。作为官方授权代理商,贞光科技通过专业技术团队和完善供应链,让这款国产存储器更好地服务国内汽车厂商。本文将探讨车载AI算力需求现状及贞光科技如何通过紫光国芯LPDDR4产品满足市场需求。 车载AI算力需求激增的背景与挑战智能驾驶推动算力需求爆发式
    贞光科技 2025-05-07 16:54 128浏览
  • 浪潮之上:智能时代的觉醒    近日参加了一场课题的答辩,这是医疗人工智能揭榜挂帅的国家项目的地区考场,参与者众多,围绕着医疗健康的主题,八仙过海各显神通,百花齐放。   中国大地正在发生着激动人心的场景:深圳前海深港人工智能算力中心高速运转的液冷服务器,武汉马路上自动驾驶出租车穿行的智慧道路,机器人参与北京的马拉松竞赛。从中央到地方,人工智能相关政策和消息如雨后春笋般不断出台,数字中国的建设图景正在智能浪潮中徐徐展开,战略布局如同围棋
    广州铁金刚 2025-04-30 15:24 375浏览
  • UNISOC Miracle Gaming奇迹手游引擎亮点:• 高帧稳帧:支持《王者荣耀》等主流手游90帧高画质模式,连续丢帧率最高降低85%;• 丝滑操控:游戏冷启动速度提升50%,《和平精英》开镜开枪操作延迟降低80%;• 极速网络:专属游戏网络引擎,使《王者荣耀》平均延迟降低80%;• 智感语音:与腾讯GVoice联合,弱网环境仍能保持清晰通话;• 超高画质:游戏画质增强、超级HDR画质、游戏超分技术,优化游戏视效。全球手游市场规模日益壮大,游戏玩家对极致体验的追求愈发苛刻。紫光展锐全新U
    紫光展锐 2025-05-07 17:07 171浏览
  • 你是不是也有在公共场合被偷看手机或笔电的经验呢?科技时代下,不少现代人的各式机密数据都在手机、平板或是笔电等可携式的3C产品上处理,若是经常性地需要在公共场合使用,不管是工作上的机密文件,或是重要的个人信息等,民众都有防窃防盗意识,为了避免他人窥探内容,都会选择使用「防窥保护贴片」,以防止数据外泄。现今市面上「防窥保护贴」、「防窥片」、「屏幕防窥膜」等产品就是这种目的下产物 (以下简称防窥片)!防窥片功能与常见问题解析首先,防窥片最主要的功能就是用来防止他人窥视屏幕上的隐私信息,它是利用百叶窗的
    百佳泰测试实验室 2025-04-30 13:28 638浏览
  • 多功能电锅长什么样子,主视图如下图所示。侧视图如下图所示。型号JZ-18A,额定功率600W,额定电压220V,产自潮州市潮安区彩塘镇精致电子配件厂,铭牌如下图所示。有两颗螺丝固定底盖,找到合适的工具,拆开底盖如下图所示。可见和大部分市场的加热锅一样的工作原理,手绘原理图,根据原理图进一步理解和分析。F1为保险,250V/10A,185℃,CPGXLD 250V10A TF185℃ RY 是一款温度保险丝,额定电压是250V,额定电流是10A,动作温度是185℃。CPGXLD是温度保险丝电器元件
    liweicheng 2025-05-05 18:36 253浏览
  • 二位半 5线数码管的驱动方法这个2位半的7段数码管只用5个管脚驱动。如果用常规的7段+共阳/阴则需要用10个管脚。如果把每个段看成独立的灯。5个管脚来点亮,任选其中一个作为COM端时,另外4条线可以单独各控制一个灯。所以实际上最多能驱动5*4 = 20个段。但是这里会有一个小问题。如果想点亮B1,可以让第3条线(P3)置高,P4 置低,其它阳极连P3的灯对应阴极P2 P1都应置高,此时会发现C1也会点亮。实际操作时,可以把COM端线P3设置为PP输出,其它线为OD输出。就可以单独控制了。实际的驱
    southcreek 2025-05-07 15:06 186浏览
  • 想不到短短几年时间,华为就从“技术封锁”的持久战中突围,成功将“被卡脖子”困境扭转为科技主权的主动争夺战。众所周知,前几年技术霸权国家突然对华为发难,导致芯片供应链被强行掐断,海外市场阵地接连失守,恶意舆论如汹涌潮水,让其瞬间陷入了前所未有的困境。而最近财报显示,华为已经渡过危险期,甚至开始反击。2024年财报数据显示,华为实现全球销售收入8621亿元人民币,净利润626亿元人民币;经营活动现金流为884.17亿元,同比增长26.7%。对比来看,2024年营收同比增长22.42%,2023年为7
    用户1742991715177 2025-05-02 18:40 209浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦