徒手编写了一个STM8的反汇编工具

原创 电子工程世界 2023-07-26 07:30
最近打算玩一下STM8, 只为了消化一下我的库存,因为我曾经买过几个型号的STM8单片机,但是一直没用来DIY啥。我对STM8熟悉程度远不如STM32,  后者是流行广泛的ARM核,STM8却是ST独家的架构。
STM8 CPU是在ST7基础上增强,有人说是从6502演变来的,我看倒也不像。
学习了一下历史,Motorola的6800演变出来的6805/6811/6809三个分支,以及6502这个与6800有渊源的CPU,从寄存器和指令集上看STM8是和它们有相似之处的,不过差异的地方也很大。作为一个8位MCU,STM8的寻址范围居然达到16M byte(我不信ST会给8位机配上1M以上的ROM或RAM),寻址模式就很多了,间接内存访问比x86都复杂,看惯了RISC的CPU更不能忍。好吧,虽然指令集复杂,STM8的执行速度还快,反正不会纯用汇编来开发。
ST并没有提供STM8的C编译器(汇编器是有的),需要用第三方的。Cosmic C编译器有免费License的版本可以用,这也是ST推荐的,我就装了一个来试。ST官方支持的还有Raisonance的编译器,此外IAR也有STM8的开发环境。
试写了个C程序测试,可以用STVP连接ST-Link下载程序,但我觉得还需要个能反汇编看编译结果的东西。Cosmic工具链里面没有反汇编程序,ST的汇编工具里也没有,STVD既然能跟踪调试应该有,但我没能把它用起来。
干脆自己写一个STM8反汇编工具吧,也练下手怎么写。先研究下STM8的指令集,这是一种典型变长指令集,除了前缀字节,操作码就在一个字节里面。于是我照着手册统计了一张表出来:

 
一个字节能表示的范围除了 0x90, 0x91, 0x92, 0x72 用来做指令前缀,其它几乎都用来作操作码了。当然许多指令都有多种寻址模式的(比如加法是谁和谁相加,需要指定),因此用了不止一个操作码。算上寻址模式,256种指令都不够用的,所以STM8靠前面增加前缀字节来扩展。从手册里面截一个例子如下(这是XOR指令的多种编码):



在指令的操作码后面就是提供数据或地址的字节了,长度由操作码加上前缀来决定。

编写反汇编程序就是写一个根据字节数据流的查表过程。上面我做的那个表只是划分了指令的分布,涉及到寻址模式的细节还是得一边写一边查手册。从表上看,操作码的高半字节大概可以把指令划分为几类,再用低半字节去细分指令,于是我的程序解码第一步就是一个 switch-case 结构来划分任务:


  1. int decode_instr(unsigned char opcode)

  2. {

  3.     switch(opcode>>4)

  4.     {

  5.         case 1: case 0x0A: case 0x0B: case 0x0C:

  6.         case 0x0D: case 0x0E: case 0x0F:

  7.             return decode_group1(opcode);

  8.         case 0: case 3: case 4: case 6: case 7:

  9.             return decode_group2(opcode);

  10.         case 5:

  11.             if(Prefix==0x72)

  12.                 return decode_group2(opcode);

  13.             else

  14.                 return decode_5x(opcode);

  15.         case 8:

  16.             return decode_8x(opcode);

  17.         case 2:

  18.             return decode_2x(opcode);

  19.         case 9:

  20.             return decode_9x(opcode);

  21.         default:

  22.             return -1;

  23.     }

  24. }


解码的结果是放到全局变量里面的,返回值只代表了指令是否有效。例如,表格最右边一列的指令我是这样解析的:


  1. int decode_9x(unsigned char opcode)

  2. {

  3.     AutoXY=1;

  4.     switch(opcode&0x0f)

  5.     {

  6.         case 0: return set_prefix(0x90);

  7.         case 1: return set_prefix(0x91);

  8.         case 2: return set_prefix(0x92);

  9.         case 3: format(0, LDW, regX, regY);

  10.                 format(0x90, LDW, regY, regX);

  11.                 return 1;

  12.         case 4: format(0, LDW, regSP, regX);

  13.                 return 1;

  14.         case 5: format(0, LD, regXH, regA);

  15.                 return 1;

  16.         case 6: format(0, LDW, regX, regSP);

  17.                 return 1;

  18.         case 7: format(0, LD, regXL, regA);

  19.                 return 1;

  20.         case 8: format(0, RCF, 0, 0);

  21.                 return 1;

  22.         case 9: format(0, SCF, 0, 0);

  23.                 return 1;

  24.         case 0xA: format(0, RIM, 0, 0);

  25.                 return 1;

  26.         case 0xB: format(0, SIM, 0, 0);

  27.                 return 1;

  28.         case 0xC: format(0, RVF, 0, 0);

  29.                 return 1;

  30.         case 0xD: format(0, NOP, 0, 0);

  31.                 return 1;

  32.         case 0xE: format(0, LD, regA, regXH);

  33.                 return 1;

  34.         case 0xF: format(0, LD, regA, regXL);

  35.                 return 1;

  36.         default:

  37.             return -1;

  38.     }

  39. }


主要是靠 format() 函数根据当前的指令前缀来翻译操作码:指令名称,寻址的第一操作数、第二操作数。若一共写 256 个 case 分支就太繁琐了,需要抓住共性,像表格中绿色背景的这一组指令我是这么处理的:


  1. int decode_group2(unsigned char opcode)

  2. {

  3.     int instr;

  4.     AutoXY=1;

  5.     switch(opcode&0x0f)

  6.     {

  7.         case 1:

  8.             switch(opcode>>4)

  9.             {

  10.                 case 0: format(0, RRWA, regX, 0); return 1;

  11.                 case 3: format(0, EXG, regA, longmem); return 1;

  12.                 case 4: format(0, EXG, regA, regXL); return 1;

  13.                 case 6: format(0, EXG, regA, regYL); return 1;

  14.                 default: return -1;

  15.             }

  16.             break;

  17.         case 2:

  18.             switch(opcode>>4)

  19.             {

  20.                 case 0: format(0, RLWA, regX, 0); return 1;

  21.                 case 3: format(0, POP, longmem, 0); return 1;

  22.                 case 4: format(0, MUL, regX, regA); return 1;

  23.                 case 6: format(0, DIV, regX, regA); return 1;

  24.                 case 7: return set_prefix(0x72);

  25.             }

  26.             break;

  27.         case 5:

  28.             switch(opcode>>4)

  29.             {

  30.                 case 3: format(0, MOV, longmem, imm8); return 1;

  31.                 case 4: format(0, MOV, mem, mem); return 1;

  32.                 case 6: format(0, DIVW, regX, regY); return 1;

  33.                 default: return -1;

  34.             }

  35.             break;

  36.         case 0xB:

  37.             switch(opcode>>4)

  38.             {

  39.                 case 3: format(0, PUSH, longmem, 0); return 1;

  40.                 case 4: format(0, PUSH, imm8, 0); return 1;

  41.                 case 6: format(0, LD, offSP, regA); return 1;

  42.                 case 7: format(0, LD, regA, offSP); return 1;

  43.                 default: return -1;

  44.             }

  45.             break;

  46.         case 0:  instr=NEG; break;

  47.         case 3:  instr=CPL; break;

  48.         case 4:  instr=SRL; break;

  49.         case 6:  instr=RRC; break;

  50.         case 7:  instr=SRA; break;

  51.         case 8:  instr=SLL; break;

  52.         case 9:  instr=RLC; break;

  53.         case 0xA:instr=DEC; break;

  54.         case 0xC:instr=INC; break;

  55.         case 0xD:instr=TNZ; break;

  56.         case 0xE:instr=SWAP; break;

  57.         case 0xF:instr=CLR; break;

  58.         default: return -1;

  59.     }

  60.     switch(opcode>>4)

  61.     {

  62.         case 0: format(0, instr, offSP, 0); return 1;

  63.         case 3: format(0, instr, mem, 0);

  64.                 format(0x92, instr, shortptr, 0);

  65.                 format(0x72, instr, longptr, 0);

  66.                 return 1;

  67.         case 4: format(0, instr, regA, 0);

  68.                 format(0x72, instr, longoffX, 0);

  69.                 return 1;

  70.         case 5: format(0x72, instr, longmem, 0);

  71.                 return 1;

  72.         case 6: format(0, instr, offX, 0);

  73.                 format(0x92, instr, sptr_offX, 0);

  74.                 format(0x72, instr, lptr_offX, 0);

  75.                 format(0x91, instr, sptr_offY, 0);

  76.                 return 1;

  77.         case 7: format(0, instr, indX, 0);

  78.                 return 1;

  79.         default: return -1;

  80.     }

  81. }


在给 format() 这个函数的参数中,指令和操作数类型都是用数值来表示的——用 enum 定义:


  1. #define SI_BASE 100

  2. #define SA_BASE 1000

  3. enum{

  4.     ADC=SI_BASE, ADD, ADDW, AND, BCCM, BCP, BCPL, BREAK, BRES, BSET, BTJF, BTJT, CALL,

  5.     CALLF, CALLR, CCF, CLR, CLRW, CP, CPW, CPL, CPLW, DEC, DECW, DIV, DIVW, EXG,

  6.     EXGW, HALT, INC, INCW, INT, IRET, JP, JPF, JRA,

  7.     JRC, JREQ, JRF, JRH, JRIH, JRIL, JRM, JRMI, JRNC, JRNE, JRNH, JRNM, JRNV,

  8.     JRPL, JRSGE, JRSGT, JRSLE, JRSLT, JRT, JRUGE, JRUGT, JRULE, JRULT, JRV,

  9.     LD, LDF, LDW, MOV, MUL, NEG, NEGW, NOP, OR, POP, POPW, PUSH, PUSHW, RCF, RET,

  10.     RETF, RIM, RLC, RLCW, RLWA, RRC, RRCW, RRWA, RVF, SBC, SCF, SIM, SLL, SLLW,

  11.     SRA, SRAW, SRL, SRLW, SUB, SUBW, SWAP, SWAPW, TNZ, TNZW, TRAP, WFE, WFI, XOR

  12. };


  13. enum{

  14.     regA=SA_BASE, regX, regY, regXH, regXL, regYH, regYL, regCC, regSP,

  15.     imm8, imm16, rel, mem, longmem, offX, offY, offSP, longoffX, longoffY,

  16.     indX, indY, shortptr, longptr, sptr_offX, sptr_offY, lptr_offX, lptr_offY,

  17.     ext, extoffX, extoffY

  18. };


我想这么写而不是直接写字符串的原因是,字符串万一写错了很难检查出来。写成常量编译器可以检查,再统一对应到字符串即可。

format() 函数是这么实现的:


  1. void format(unsigned char pre, int instr, int opr1, int opr2)

  2. {

  3.     char replace=(AutoXY && Prefix==0x90 && pre==0);

  4.     if(replace)

  5.     {

  6.         int r1, r2;

  7.         r1=replace_X_Y(opr1);

  8.         r2=replace_X_Y(opr2);

  9.         if(r1>=SA_BASE && r2==0)

  10.             opr1=r1;

  11.         else

  12.         {

  13.             if(r2>=SA_BASE && r1==0)

  14.                 opr2=r2;

  15.             else

  16.                 return;

  17.         }

  18.     }

  19.     if(Prefix==pre ||replace)

  20.     {

  21.         if(instr

  22.             Str_inst="INVALID";

  23.         else

  24.             Str_inst=SYMI(instr);

  25.         if(opr1

  26.             Str_opr1=Empty;

  27.         else

  28.             Str_opr1=SYMA(opr1);

  29.         if(opr2

  30.             Str_opr2=Empty;

  31.         else

  32.             Str_opr2=SYMA(opr2);

  33.     }

  34. }


format() 函数检查匹配指令前缀,匹配上了才把数值表示的指令和操作数类型转换成字符串,分别存到三个全局变量 Str_inst, Str_opr1, Str_opr2 中,其实这些字符串都是定义好的,也就是写指针而已。有个特殊处理是在 0x90 指令前缀下,自动将 X 寄存器替换为 Y 寄存器。

再来看下主程序中怎么输出反汇编文本的,首先是初始换化几个全局变量,然后调用 decode_instr() 按照操作码分解指令,判断是否成功。如果遇到指令前缀,那么就重新取下一个字节;如果有前缀但指令未被识别,那么调用 decode_instr_special() 进行特殊指令的处理,也就是上面的表中没法表示出来的指令。若解码失败,先输出错误的信息。


  1.     for(p=code;p

  2.     {

  3.         if(Prefix==0)

  4.             printf("%5X:\t", base_addr+(p-code));

  5.         Str_inst=Empty;

  6.         Str_opr1=Empty;

  7.         Str_opr2=Empty;

  8.         BitOpr=0;

  9.         RevOpr=0;

  10.         AutoXY=0;

  11.         tmp=decode_instr(*p);

  12.         if(tmp==0)

  13.         {   // prefix set

  14.             printf("%02X ", Prefix);

  15.             continue;

  16.         }

  17.         if(tmp>0 && Str_inst==Empty && Prefix)

  18.             tmp=decode_instr_special(*p);

  19.         if(tmp==-1)

  20.         {

  21.             if(Prefix==0)

  22.                 printf("   ");

  23.             printf("%02X ", *p);

  24.             printf("   ????????   Unknown");

  25.         }


下面就是解码成功后的翻译过程了,用 get_extra() 函数从代码数据中提取操作数(立即数、地址等),存放到dat1, dat2两个整型数,供后面用 printf() 输出。至于 printf() 需要的格式字符串,实际上是由解码得到的 Str_opr1, Str_opr2 结果提供的。这里还要特殊处理一下带位操作的指令(BCCM, BCPL, BRES, BSET, BTJF, BTJT这几个),其中的位是编码在操作码当中的,我在 format() 函数中并不把这个位编码作为一个操作数,尽管从汇编语言角度它应该是算一个操作数。


  1.         else  // OK

  2.         {

  3.             int nx;

  4.             int i;

  5.             unsigned int dat1, dat2, arg1, arg2;

  6.             char bitpos[]=", #n";

  7.             char fmt_str[64];


  8.             nx=get_extra(p, &dat1, &dat2);

  9.             if(Str_opr1==SYMA(rel))

  10.             {

  11.                 signed char offset=dat1;

  12.                 dat1=base_addr+(p-code)+nx+1+offset;

  13.             }


  14.             if(Prefix==0)

  15.                 printf("   ");

  16.             for(i=0;i<1+nx;i++)

  17.                 printf("%02X ", p[i]);

  18.             for(;i<5;i++)

  19.                 printf("   ");


  20.             if(BitOpr)

  21.                 bitpos[3]='0'+(*p>>1&7);

  22.             else

  23.                 bitpos[0]=0;


  24.             if(Str_opr1!=Empty)

  25.             {

  26.                 if(Str_opr2==Empty) // one oprand

  27.                 {

  28.                     sprintf(fmt_str, "%s %s%s",Str_inst, Str_opr1, bitpos);

  29.                     arg1=dat1;

  30.                 }

  31.                 else

  32.                 {

  33.                     if(RevOpr)

  34.                     {

  35.                         sprintf(fmt_str, "%s %s%s, %s",Str_inst, Str_opr2, bitpos, Str_opr1);

  36.                         if(strchr(Str_opr2,'%'))

  37.                         {

  38.                             arg1=dat2;

  39.                             arg2=dat1;

  40.                         }

  41.                         else

  42.                             arg1=dat1;

  43.                     }

  44.                     else

  45.                     {

  46.                         sprintf(fmt_str, "%s %s%s, %s",Str_inst, Str_opr1, bitpos, Str_opr2);

  47.                         if(strchr(Str_opr1,'%'))

  48.                         {

  49.                             arg1=dat1;

  50.                             arg2=dat2;

  51.                         }

  52.                         else

  53.                             arg1=dat2;

  54.                     }

  55.                 }

  56.             }

  57.             else

  58.                 strcpy(fmt_str, Str_inst);


  59.             printf(fmt_str, arg1, arg2);


  60.             p+=nx;

  61.         }

  62.         Prefix=0;

  63.         printf("\n");


完整的源程序可点击下方阅读原文下载。贴一个运行的结果:反汇编内容是我写的LED点灯测试程序,不包括中断向量表。


 


暂时还不能确定指令有没有遗漏,后面边用边检查吧。


推荐阅读

国内外新增GaN项目盘点
矛头对准自动驾驶汽车,美再向中国发难!
关于芯片战争的二三事
中国半导体行业协会发声:美政府近年采取一系列限制措施令人遗憾

众号内回复您想搜索的任意内容,如问题关键字、技术名词、bug代码等,就能轻松获得与之相关的专业技术内容反馈。快去试试吧!

如果您想经常看到我们的文章,可以进入我们的主页,点击屏幕右上角“三个小点”,点击“设为星标”。


欢迎扫码关注


电子工程世界 关注EEWORLD电子工程世界,即时参与讨论电子工程世界最火话题,抢先知晓电子工程业界资讯。
评论 (0)
  • 一、引言:智能化趋势下的学爬玩具开发挑战随着早教理念的普及,学爬玩具作为婴幼儿早期运动能力开发的重要工具,市场需求持续增长。然而,传统学爬玩具开发面临多重挑战:需集成红外遥控、语音交互、电机控制等多模块,开发周期长、硬件成本高;复杂的红外编解码与语音功能实现依赖工程师深度参与,技术门槛陡增。如何以更低成本、更快速度打造差异化产品,成为行业亟待解决的痛点。二、传统开发模式痛点分析硬件冗余红外接收模块、语音芯片、主控MCU分立设计,导致PCB面积增加,BOM成本攀升。开发周期长需工程师独立完成红外协
    广州唯创电子 2025-04-16 08:40 49浏览
  • 展会名称:2025成都国际工业博览会(简称:成都工博会)展会日期:4月23 -25日展会地址:西部国际博览城展位号:15H-E010科士威传动将展示智能制造较新技术及全套解决方案。 2025年4月23-25日,中国西部国际博览城将迎来一场工业领域的年度盛会——2025成都国际工业博览会。这场以“创链新工业,共碳新未来”为主题的展会上,来自全球的600+ 家参展企业将齐聚一堂,共同展示智能制造产业链中的关键产品及解决方案,助力制造业向数字化、网络化、智能化转型。科士威传动将受邀参展。&n
    科士威传动 2025-04-14 17:55 90浏览
  •   无人装备作战协同仿真系统软件:科技的关键支撑   无人装备作战协同仿真系统软件,作为一款综合性仿真平台,主要用于模拟无人机、无人车、无人艇等无人装备在复杂作战环境中的协同作战能力、任务规划、指挥控制以及性能评估。该系统通过搭建虚拟战场环境,支持多种无人装备协同作战仿真,为作战指挥、装备研发、战术训练和作战效能评估,提供科学依据。   应用案例   系统软件供应可以来这里,这个首肌开始是幺伍扒,中间是幺幺叁叁,最后一个是泗柒泗泗,按照数字顺序组合就可以找到。   核心功能   虚拟战
    华盛恒辉l58ll334744 2025-04-14 17:24 90浏览
  • 你知道精益管理中的“看板”真正的意思吗?在很多人眼中,它不过是车间墙上的一块卡片、一张单子,甚至只是个用来控制物料的工具。但如果你读过大野耐一的《丰田生产方式》,你就会发现,看板的意义远不止于此。它其实是丰田精益思想的核心之一,是让工厂动起来的“神经系统”。这篇文章,我们就带你一起从这本书出发,重新认识“看板”的深层含义。一、使“看板”和台车结合使用  所谓“看板”就是指纸卡片。“看板”的重要作用之一,就是连接生产现场上道工序和下道工序的信息工具。  “看板”是“准时化”生产的重要手段,它总是要
    优思学院 2025-04-14 15:02 118浏览
  • 二、芯片的设计1、芯片设计的基本流程 (1)需求定义: 明确芯片功能(如处理器、存储、通信)、性能指标(速度、功耗、面积)及目标应用场景(消费电子、汽车、工业)。 (2)架构设计: 确定芯片整体框架,包括核心模块(如CPU、GPU、存储单元)的协同方式和数据流路径。 (3)逻辑设计: 通过硬件描述语言(如Verilog、VHDL)将架构转化为电路逻辑,生成RTL(寄存器传输级)代码。 (4)物理设计: 将逻辑代码映射到物理布局,涉及布局布线、时序优化、功耗分析等,需借助EDA工具(如Ca
    碧海长空 2025-04-15 11:30 174浏览
  • 一、智能门锁市场痛点与技术革新随着智能家居的快速发展,电子门锁正从“密码解锁”向“无感交互”进化。然而,传统人体感应技术普遍面临三大挑战:功耗高导致续航短、静态人体检测能力弱、环境适应性差。WTL580微波雷达解决方案,以5.8GHz高精度雷达感知技术为核心,突破行业瓶颈,为智能门锁带来“精准感知-高效触发-超低功耗”的全新交互范式。二、WTL580方案核心技术优势1. 5.8GHz毫米波雷达:精准感知的革命全状态人体检测:支持运动、微动(如呼吸)、静态(坐卧)多模态感知,检测灵敏度达0.1m/
    广州唯创电子 2025-04-15 09:20 98浏览
  • 一、芯片的发展历程总结:1、晶体管的诞生(1)电子管时代 20世纪40年代,电子管体积庞大、功耗高、可靠性差,无法满足计算机小型化需求。(2)晶体管时代 1947年,贝尔实验室的肖克利、巴丁和布拉顿发明点接触晶体管,实现电子信号放大与开关功能,标志着固态电子时代的开端。 1956年,肖克利发明晶体管。(3)硅基晶体管时代 早期晶体管采用锗材料,但硅更耐高温、成本低,成为主流材料。2、集成电路的诞生与发展 1958年,德州仪器工程师基尔比用锗材料制成世界上第一块含多个晶体管的集成电路,同年仙童半导
    碧海长空 2025-04-15 09:30 127浏览
  • 2025年4月13日(中国武汉)——在全球经济分化与地缘政治不确定性加剧的背景下,科技与金融的深度融合已成为推动创新与繁荣的关键动力。为实现科技创新、产业进步和金融发展有机结合,发挥金融对科技创新和产业进步的支持作用,国际金融论坛(IFF)科技金融委员会启动大会暨首届科技金融圆桌会议于4月13日在湖北省武汉市武汉产业创新发展研究院成功举行。同时,IFF科技金融委员会由国际金融论坛IFF与武创院联合成立。本次大会汇聚了来自政府、产业与学术研究机构及金融等多领域的精英,共同探讨科技金融如何更好地服务
    华尔街科技眼 2025-04-15 20:53 46浏览
  • 四、芯片封测技术及应用场景1、封装技术的发展历程 (1)DIP封装:早期分立元件封装,体积大、引脚少; (2)QFP封装:引脚密度提升,适用于早期集成电路。 (3)BGA封装:高密度互连,散热与信号传输优化; (4)3D封装:通过TSV(硅通孔)实现垂直堆叠,提升集成度(如HBM内存堆叠); (5)Chiplet封装:异质集成,将不同工艺节点的模块组合(如AMD的Zen3+架构)。 (6)SiP封装:集成多种功能芯片(如iPhone的A系列SoC整合CPU、GPU、射频模块)。2、芯片测试 (1
    碧海长空 2025-04-15 11:45 217浏览
  • 一、智能语音播报技术演进与市场需求随着人工智能技术的快速发展,TTS(Text-to-Speech)技术在商业场景中的应用呈现爆发式增长。在零售领域,智能收款机的语音播报功能已成为提升服务效率和用户体验的关键模块。WT3000T8作为新一代高性能语音合成芯片,凭借其优异的处理能力和灵活的功能配置,正在为收款机智能化升级提供核心技术支持。二、WT3000T8芯片技术特性解析硬件架构优势采用32位高性能处理器(主频240MHz),支持实时语音合成与多任务处理QFN32封装(4x4mm)实现小型化设计
    广州唯创电子 2025-04-15 08:53 111浏览
  • 在当今汽车电子化和智能化快速发展的时代,车规级电子元器件的质量直接关系到汽车安全性能。三星作为全球领先的电子元器件制造商,其车规电容备受青睐。然而,选择一个靠谱的三星车规电容代理商至关重要。本文以行业领军企业北京贞光科技有限公司为例,深入剖析如何选择优质代理商。选择靠谱代理商的关键标准1. 授权资质与行业地位选择三星车规电容代理商首先要验证其授权资质及行业地位。北京贞光科技作为中国电子元器件行业的领军者,长期走在行业前沿,拥有完备的授权资质。公司专注于市场分销和整体布局,在电子元器件领域建立了卓
    贞光科技 2025-04-14 16:18 152浏览
  • 一、引言:健康管理数字化浪潮下的血压监测转型在慢性病高发与老龄化加剧的双重压力下,家庭健康监测设备正从“被动测量工具”向“主动健康管家”演进。传统血压计虽能提供基础数值,却无法解决用户的核心痛点:数据如何解读?异常如何干预?风险如何预防?WT2605C芯片方案的诞生,通过“AI对话+云端互联+个性化服务”三重技术突破,重新定义了血压计的价值边界——它不仅是一台测量仪器,更是一个全天候在线的健康管理生态系统。二、传统血压计的局限与用户需求升级1. 功能单一性困境数据孤岛:仅显示收缩压/舒张压数值,
    广州唯创电子 2025-04-16 08:55 52浏览
  •   高空 SAR 目标智能成像系统软件:多领域应用的前沿利器   高空 SAR(合成孔径雷达)目标智能成像系统软件,专门针对卫星、无人机等高空平台搭载的 SAR传感器数据,融合人工智能与图像处理技术,打造出的高效目标检测、识别及成像系统。此软件借助智能算法,显著提升 SAR图像分辨率、目标特征提取能力以及实时处理效率,为军事侦察、灾害监测、资源勘探等领域,提供关键技术支撑。   应用案例系统软件供应可以来这里,这个首肌开始是幺伍扒,中间是幺幺叁叁,最后一个是泗柒泗泗,按照数字顺序组合
    华盛恒辉l58ll334744 2025-04-14 16:09 155浏览
  • 三、芯片的制造1、制造核心流程 (1)晶圆制备:以高纯度硅为基底,通过拉晶、切片、抛光制成晶圆。 (2)光刻:光刻、离子注入、薄膜沉积、化学机械抛光。 (3)刻蚀与沉积:使用干法刻蚀(等离子体)精准切割图形,避免侧壁损伤。 (4)掺杂:注入离子形成PN结特性,实现晶体管开关功能。2、材料与工艺创新 (1)新材料应用: 高迁移率材料(FinFET中的应变硅、GaN在射频芯片中的应用); 新型封装技术(3D IC、TSV硅通孔)提升集成度。 (2)工艺创新: 制程从7nm到3nm,设计架构由F
    碧海长空 2025-04-15 11:33 217浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦