news 2026/10/5 11:00:01

NXP MCU CAN位时间配置详解:从采样点到波特率实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NXP MCU CAN位时间配置详解:从采样点到波特率实战指南

1. 为什么CAN波特率配置总在“能用”和“不好用”之间反复

1. 1 先讲一个我踩了半晚上的案例教训

早年间给客户做一块基于NXP S32K144的控制器底板,CAN0挂整车动力链,目标速率500kbps。当时我按老例把预分频器一填,波特率寄存器看起来天衣无缝,环回测试也全过。结果一接上总线,两个节点相互之间错误帧就像放鞭炮一样停不下来,状态寄存器里Error Passive警告刷得飞起。后来拿示波器一测,发现发送节点实际跑在497.8kbps左右,差的百分比倒不算出格,但接收节点的采样点却恰好落在位流边沿附近,总线位同步根本来不及收敛。这件事给我的教训是:CAN波特率从来不是“算个整数分频”这么简单,位时间(Bit Time)里的每个段、每个Tq怎么分配,直接决定了这条总线在复杂工况下稳不稳。

这也是我写这篇NXP MCU CAN位时间配置详解的原因。很多刚接触CAN总线的工程师,第一反应是找波特率计算公式,套进去得到一组寄存器值,然后烧录、测通、完事。但CAN作为一个多主、带仲裁的串行总线,它的物理层非常简单,却把大量“时序对齐”的重任压在了每个节点自己的位时间配置上。你查资料时会看到一句话:CAN位时间的划分,比单纯算波特率更重要。这句话不是危言耸听,等你在现场被偶发错帧折磨两天后就懂了。

本文适合三类人:刚接手NXP MCU的嵌入式工程师、被CAN调试折磨过的软硬件协同开发者,以及准备做平台化CAN驱动的人。我会从位时间的每一个Tq讲起,把FlexCAN的寄存器字段、时钟源选择、采样点设计、实际配置案例都过一遍,最后把我在现场踩过的坑和排查方法一并列出来,省得你走我当年的冤枉路。

1. 2 位时间配置的“软性验收标准”

CAN总线的速率验收,不能只看波特率是否等于目标值。更关键的是三件事:每一位能不能在最佳时刻被采样、节点间时钟误差能不能通过重同步消化掉、传播段能否覆盖整条链路的延迟。换句话说,波特率只是及格线,位时间分配才是决定“能不能长期稳定跑”的硬指标。

我经常打一个比方:波特率是你设定“闹钟每隔一秒钟响一次”,位时间则是“闹钟在每秒钟的哪个毫秒发出声音”。如果声音恰好落在你最容易醒的位置,哪怕时钟有点漂,你也能准时醒来;如果声音卡在边缘,稍微有点偏差就直接错过。CAN的采样点就是那个“最容易醒的位置”,而传播段、相位段、SJW就是在帮你排查各种“意外迟醒”的缓冲机制。

2. 位时间到底由什么组成:四个段加一个采样点

2. 1 Tq:位时间的最小刻度

CAN中每一位并不是一个简单的“高电平持续多久”的方块,而是被划分成若干个最小时间单元,这个最小时间单元就是Tq(Time Quantum)。位时间与Tq的关系可以用一个直白的公式表达:

波特率 = 1 /(每位Tq数量 × 单个Tq时长)

单个Tq时长 = CAN外设时钟周期 × 预分频值。

这个公式小学算术水平,但最容易埋雷的就是“预分频值”四个字。在NXP的FlexCAN里,寄存器字段叫PRESDIV,写入0表示除以1,写入3表示除以4。如果你读代码时看到类似baudrate = can_clk / ((presdiv + 1) * (1 + propseg + pseg1 + pseg2))这样的计算,这里的presdiv、propseg、pseg1、pseg2如果没加1,算出来的数往往离目标速率差着一小截。偏偏这一小截,可能就是两个节点互相丢帧的元凶。

很多厂家SDK的驱动函数会把“寄存器值+1”这件事封装好,所以你在API层往往感知不到。但如果哪天你直接读写寄存器,或者从别的平台移植代码,就必须反复核对这个+1。我见过不止一个项目,最后查出来的问题根本不是硬件信号质量,而是有人在初始化里手写了一个波特率表,填值的时候照着数据手册的“实际Tq数”误当成“寄存器值”填进去了。

2. 2 同步段、传播段、相位段1、相位段2分别干什么

按CAN参考模型,每个位时间至少分成四段:

  • 同步段(Sync Segment):固定1个Tq,用于检测边沿,控制器在这个段结束的位置开始采样判断;
  • 传播段(Propagation Segment):用于补偿信号在总线上的传输延迟和收发器延迟,工程上建议取值要覆盖“2倍最长总线延迟+收发器收发延迟”;
  • 相位段1(Phase Segment 1):在采样点之前,吸收因不同节点时钟频率差造成的相位误差;
  • 相位段2(Phase Segment 2):在采样点之后,同样给相位误差留出缓冲。

重同步跳转宽度(SJW)不属于段,但决定了当节点发现边沿漂移后,每次最多能“吞掉”或“吐出”多少个Tq。你可以把它理解成程序员手里的“重试窗口”:窗口越大,越能容忍对方时钟的偏差;但窗口也不能无限大,因为它占用的是相位段的缓冲空间。

很多新手会把PSEG1/PSEG2当成“波特率凑数区”,只要最后总Tq能除尽就交差。实际恰恰相反,传播段和相位段的分配决定采样点位置,而采样点位置直接决定整个总线的抗干扰能力。见过不少调试现场:波特率精确到小数点后三位,可采样点跑到52%,结果线缆一拉长就偶发错帧。CAN规范要求的不只是速率精确,更是在噪声和传播延迟下仍能稳定采样。

2. 3 采样点:最容易被遗漏的“隐藏指标”

采样点位置计算公式如下:

采样点 =(同步段 + 传播段 + 相位段1)/ 每位Tq总数 × 100%

工程上的推荐值:经典CAN通常在75%~87.5%之间,很多车厂干脆直接要求87.5%;工业现场如果线缆不长、节点不多,75%~80%也够用;CAN FD的仲裁段建议80%以上,数据段建议75%~85%。

采样点离位开始太近,意味着你读到的大部分还是前一位的电平过渡;采样点离位结束太近,又意味着几乎没有给相位段2留下补偿空间。我在实际调试中的经验是:采样点数值越高,对总线传播延迟的容忍度越好;但高采样点往往需要较大的PSEG1和PROPSEG值,而这两者都有硬件上限。这时候就要根据实际管脚约束做取舍,而不是死背一个百分比。

2. 4 CAN FD对位时间做了什么改变

CAN FD引入了双位时序的概念:仲裁段沿用经典CAN的位时间配置,数据段则使用独立的位时序寄存器。在NXP的S32K1系列、i.MX RT系列带FD功能的FlexCAN外设上,仲裁段由CTRL1等寄存器控制,数据段由FDCBT寄存器控制。配置时最容易犯的错是只改仲裁段速率,数据段不动,结果FD报文一到数据段就全部变成格式错误。

更麻烦的是,很多工程师会把配置经典CAN 500kbps的那套“寄存器值”直接套到CAN FD的仲裁段上,觉得“反正都是500k”。实际上,如果激活了FD功能,FDCBT里数据段位时序没配,或配置值小于硬件允许的最小Tq,控制器在收到BRS位后就会立刻产生错误。后面实操部分我会专门给出一种配置思路。

3. NXP MCU的CAN硬件模块:FlexCAN怎么吃下这些参数

3. 1 同一套IP,遍布Kinetis、S32K、i.MX RT

NXP的CAN外设主要分两派:一派是源自Freescale时代的FlexCAN,大量存在于S32K、Kinetis系列和i.MX RT跨界处理器上;另一派是LPC风格的传统CAN控制器,比如LPC1768这类老牌MCU。工程里遇到九成都是FlexCAN,所以本文的主角也是FlexCAN。

FlexCAN的寄存器体系高度统一:CTRL1负责经典CAN位时序,支持FD功能的芯片再加一个FDCBT寄存器。也就是说,只要把一个型号调明白了,换到另一个型号,通常只是时钟树和引脚复用不同,寄存器布局几乎不用重新学。这对常年做平台化方案的人非常友好,踩过一个坑,后续所有NXP项目都能复用同一条经验。

不过平台化也是双刃剑。同一套代码在不同芯片上编译,第一件要确认的事就是CAN外设时钟源是否一致。如果两块板子的CAN时钟源分别来自外部晶振和PLL,即使波特率目标都是500kbps,你算出来的PRESDIV、PSEG1、PSEG2也会不同。这就是为什么我说“灵活CAN位时间配置,本质是三位一体:时钟源、预分频、段分配”。

3. 2 先理顺时钟,再配置寄存器

电源启动后,第一步永远是确认CAN外设时钟源。S32K1系列在CSCMR寄存器中选择FlexCAN时钟源来自OSCCLK还是SPLL_CLK;i.MX RT的FlexCAN可以选择24MHz外部振荡器或系统IPG时钟。如果时钟源本身不是你预期值,后面所有波特率公式全白算。

我在实际项目里见过一个case:代码里写得清清楚楚16MHz外部晶振,但硬件用了8MHz,导致所有板子CAN都慢一半。软件排查了两天才发现,是启动时钟初始化把分频寄存器默认值设成了2。从那一刻起,我要求自己拿到新板子先读一遍分频寄存器,确认CAN时钟的实际频率,再谈波特率设置。

这里有个容易被跳过的细节:切换时钟源时,一定要先让CAN模块进入Freeze模式或关闭状态,再切换时钟并重新初始化。如果你在CAN报文收发过程中偷偷切换时钟,轻则导致当前位时序混乱,重则直接触发总线关闭。NXP参考手册里的推荐顺序一般是:置位MCR[FRZ]和MCR[HALT] → 等待模块冻结 → 改时钟源和位时序寄存器 → 解除冻结。

3. 3 CTRL1里的字段,以及“为什么有些位只能在Freeze模式改”

FlexCAN的CTRL1寄存器是最核心的位时序控制寄存器,我把常用字段整理成一张表,方便对照查阅:

字段位宽寄存器值范围实际Tq数说明
PRESDIV8位0~255寄存器值+1分频决定每个Tq的时长
RJW3位0~7寄存器值+1重同步跳转宽度
PSEG13位0~7寄存器值+1相位段1
PROPSEG3位0~7寄存器值+1传播段
PSEG22位0~3寄存器值+1相位段2
SMP1位0/1-采样次数,0采样一次,1为三次采样

CAN引擎在每个Tq的边界走一条状态机,随意在运行态改写CTRL1可能把状态机打乱。NXP手册会反复强调:先让模块进入Freeze模式再修改位时序。很多人初始化时明明写了寄存器,结果写入不生效,排查半天发现是没进Freeze。

另一个核心认知偏差是“寄存器写入值不等于实际段长”。例如PSEG1=7实际是8个Tq,PSEG2=2实际是3个Tq,SYNC固定1个Tq。所以如果PROPSEG=7、PSEG1=7、PSEG2=2,总位时间就是1+8+8+3=20个Tq,采样点=(1+8+8)/20=85%。这个换算关系在调CAN FD数据段时会反复用到,早养成习惯早省事。

3. 4 LPC等其他CAN控制器的BTR寄存器差异

如果你的项目用的是LPC系列或其他传统CAN控制器,寄存器结构并不叫CTRL1,而是BTR。BTR里的字段包括BRP、SJW、TSEG1、TSEG2等,公式结构本质上一样,但每个字段的位宽、含义和换算规则都跟FlexCAN不同。

这时候最危险的就是“直接复制寄存器值”。从FlexCAN移植到LPC,500kbps对应的PSEG1=7不代表LPC里填7就能得到一样的效果。我在跨平台移植时,习惯先把两边各自的段分配公式写清楚,把“目标采样点”作为移植的基准,再分别计算各自的寄存器值。只要基准一致,采样点一致,波特率一致,底层实现差异其实不影响应用稳定性。

4. 手把手配置CAN波特率:从目标速率到寄存器值

4. 1 通用配置三步走

我把整个配置流程拆成三步,无论哪个平台都适用:

  1. 确认CAN外设时钟频率,读时钟寄存器或查启动代码,不要凭想象;
  2. 用目标波特率反推每位需要的Tq总数量,然后决定预分频值;
  3. 在硬件允许的段范围内分配传播段、相位段1、相位段2,使采样点落在目标区间。

反推的时候有个经验值:每位Tq总数尽量控制在12~20之间,不要低于8。Tq太少,采样窗口太窄,对晶振精度和线缆延迟极敏感;Tq太多,看起来分频很细,但段值分配空间也受寄存器位宽限制,反而容易卡在采样点上不去。如果算出来位时间小于8,要么换更低的CAN时钟,要么提高预分频,让Tq更粗一点。

4. 2 工作样例一:S32K144,外部8MHz晶振,500kbps

先算总Tq数:8MHz ÷ 500kbps = 16,说明每位包含16个Tq。预分频寄存器写0即可,CAN时钟不预分频。

段分配我选的是:同步段1个Tq,传播段4个Tq,相位段1取7个Tq,相位段2取4个Tq,总位时间=1+4+7+4=16,采样点=(1+4+7)/16=75%。换算成寄存器值就是PRESDIV=0,PROPSEG=3,PSEG1=6,PSEG2=3,RJW=2。

有人会问,为什么不用87.5%的采样点?因为16个Tq下想要87.5%,需要传播段+相位段1合计达到13个Tq。但FlexCAN里PSEG1实际最大8、PROPSEG实际最大8,PSEG2至少要1个Tq,所以硬凑87.5%会导致超限。遇到这种硬件约束,我把采样点退到75%附近,同时把SJW拉大到3个Tq。SJW=3能让节点通过多次重同步把误差消化掉,比一个好看但无法落地的87.5%更实际。这也是网上所谓“CAN必须87.5%”不能盲信的原因:得看硬件允许的范围。

4. 3 工作样例二:S32K1 FlexCAN时钟用40MHz,500kbps

如果你用S32K1的PLL时钟,把FlexCAN时钟配到40MHz,同样的500kbps目标就变成另外一组值。

40MHz ÷ 500kbps = 80,也就是说预分频之前每位相当于80个Tq。这时我选择PRESDIV=3,也就是4分频,Tq时钟变为10MHz,每位变成20个Tq。段分配是:同步段1个Tq,传播段8个Tq,相位段1取8个Tq,相位段2取3个Tq,总位时间=1+8+8+3=20,采样点=(1+8+8)/20=85%。寄存器值就是PRESDIV=3,PROPSEG=7,PSEG1=7,PSEG2=2,RJW=2。

同样500kbps,为什么换了个时钟源,段分配全变了?因为40MHz时钟给每个Tq提供了更细的时间粒度,20个Tq里的传播段和相位段容量比16个Tq方案更充裕。这也就是为什么我强调“先确认CAN时钟,再谈寄存器值”,同一个目标波特率,在不同时钟下可能对应完全不同的最佳段组合。

4. 4 工作样例三:i.MX RT1052,24MHz外部振荡器,仲裁段1Mbps

i.MX RT系列常用的FlexCAN时钟是24MHz外部振荡器。目标1Mbps时:24MHz ÷ 1Mbps = 24,我选择PRESDIV=1,也就是2分频,Tq时钟变12MHz,每位12个Tq。

段分配:同步段1个Tq,传播段2个Tq,相位段1取7个Tq,相位段2取2个Tq,总位时间=1+2+7+2=12,采样点=(1+2+7)/12≈83.3%。寄存器值就是PRESDIV=1,PROPSEG=1,PSEG1=6,PSEG2=1,RJW=1。

如果这个芯片开了CAN FD,数据段也要单独配。比如数据段想跑2Mbps,12MHz的Tq时钟下每位只有6个Tq,已经逼近硬件下限。所以遇到CAN FD需求,我的经验是先把CAN外设时钟整体抬高,比如用系统PLL分出40MHz甚至更高,再分别算仲裁段和数据段的预分频。数据段由于速率快,对采样点尤其敏感,一般建议采样点放在75%~85%之间,并且不要用太小的PSEG2去赌收发器边沿。

4. 5 速查表与验证方法

把我上面三个样例,加上一个40MHz配1Mbps的例子,汇总成一张速查表:

场景CAN时钟目标速率PRESDIVPROPSEGPSEG1PSEG2RJW每位Tq采样点
S32K144外部晶振8MHz500kbps036321675%
S32K144 PLL40MHz500kbps377222085%
i.MX RT振荡器24MHz1Mbps116111283.3%
S32K144 PLL40MHz1Mbps332111080%

表里写的是寄存器值,不是实际Tq数,这点要特别注意。上板验证时,最直接的办法是用示波器抓CAN_H和CAN_L的差分波形,测量下降沿到下降沿的时间,这个时间就是一位的实际宽度。对500kbps来说,位宽应该是2000ns,误差在0.1%以内基本没问题。

除了示波器,逻辑分析仪带CAN解码功能的也能用,但要确保采样率足够高,否则解码出来的位宽本身就带误差。我习惯先用示波器量位宽,再用总线工具做长包压力测试,两边都通过,才敢说这条CAN的波特率配置真正合格。

4. 6 时钟源切换的隐藏坑

切换到新的CAN时钟源后,建议先做一次“听模式”自检:让节点只听不发,观察能否正确收到对端报文。如果能收但发不出去,或者一发送就报错,很可能是时钟源切换后没有重新计算波特率,或者Freeze模式退出顺序不对。

另一个隐藏坑是外部晶振启动需要时间。很多MCU上电后晶振要稳定一段时间,如果CAN初始化代码在晶振稳定前就读取了OSCCLK,读到的是尚未稳定的时钟,波特率自然不准。解决方法是明确等待晶振稳定标志,或者用PLL时钟等稳定源来做CAN时钟。这一点在低温环境下尤其明显,晶振启动时间会变长,代码里不能写死延时裸等,要用状态标志查询。

5. 常见问题与排查技巧实录

5. 1 波特率表没错,但两个节点互发还是报错

这个问题我在现场排查过很多次。表面上看,两边初始化代码的波特率参数完全一致,寄存器值也一五一十对上了,但接上线就满屏错误帧。

前三个检查点:一是CANH和CANL是否接反,收发器到座子之间有没有交叉;二是120Ω终端电阻是否在总线两端各有一个;三是两边的地电位是否一致,CAN是差分信号但收发器的共模范围有限,地电位漂移大了照样报错。

如果上面都没问题,就要怀疑CAN时钟源了。我见过最典型的情况是:两个节点“理论上”都用8MHz,但其中一个节点使用的单片机内部IRC时钟本身误差就大,而另一个用外部晶振。两者各自的绝对误差都在允许范围内,但放在一起,相对偏差就可能超过总线的同步能力。解决办法是优先用外部晶振或PLL,并复查启动代码中CAN时钟分频链路。

5. 2 采样点放在中线和放在后端,效果差别有多大

有段时间我在实验室专门做过一组对比:同一块S32K板子、同样500kbps,一组采样点配置在75%,另一组配置在85%,然后用一根十几米的CAN线连接远端节点,再在旁边开一个开关电源制造干扰。

结果是采样点75%的组在总线长度超过12米后,错误帧率明显上升;采样点85%的组同样长度下依然能维持较低错误率。原因并不神秘:采样点越靠后,越能避开位流前段的不稳定区域,相当于把判读窗拖到了电平和传播延迟相对平稳的位置。这个测试后来成为我在新项目里的固定验证流程:先在线缆最长、干扰最大的条件下测试采样点余量,再回到实验室定最终寄存器参数。

5. 3 CAN FD仲裁段和数据段配置的简易心法

CAN FD配置比经典CAN多一层:仲裁段维持1Mbps甚至500kbps,数据段可能跑到2Mbps、5Mbps甚至更高。我的简易心法是:先按经典CAN的标准把仲裁段调稳定,再单独调数据段,两个阶段分开验证,不要一次全改。

数据段的高速位时序很容易出现“配置值看着对,但实际采样点落到一个极低值”的情况。比如数据段只有10个Tq,采样点想放到80%,意味着相位段1和传播段合计要达到7个Tq,段分配空间非常紧张。这种情况下,要么提高CAN外设时钟,让数据段有更多Tq可用,要么接受75%附近的采样点,再靠SJW兜底。永远不要为了凑一个漂亮寄存器值,把相位段2压到最小,因为收发器在高速数据段本身的边沿延迟离散度就比仲裁段大。

5. 4 避坑清单:六条用错误换来的经验

  • 寄存器值不是实际Tq数,PSEG1、PSEG2、PROPSEG、RJW全部要+1再代入公式;
  • 修改CTRL1和FDCBT前,先让FlexCAN进入Freeze模式,改完再退出;
  • 确认CAN外设时钟源,外部晶振、PLL、系统时钟分频都可能影响最终波特率;
  • 长距离总线必须补传播段,否则采样点再高也救不回来;
  • CAN FD的数据段位时序要单独配置,不能沿用仲裁段寄存器值;
  • 新板子拿到手先测实际位宽,不要相信“代码里写的就是实际跑的”。

5. 5 一些趁手工具与参考资料

排查CAN波特率问题时,我的固定配置是:一台双通道示波器,一个带CAN解码的逻辑分析仪,加上一台能统计错误计数的CAN分析盒。示波器负责看物理位宽和边沿质量,逻辑分析仪负责看帧结构和错误类型,分析盒负责长时间统计错误率。三者配合,基本能区分问题是出在物理层还是链路层。

NXP官方SDK/RTD里通常提供FlexCAN波特率设置接口,比如传入外设时钟和目标波特率,驱动自动算寄存器值。但我的建议是,自己把你使用的SDK里setBaudRate函数实现翻出来看一遍,尤其注意它默认的采样点落在多少。不少SDK的默认策略不会主动优化采样点,可能只是保证“波特率正确”,并不保证“采样点合理”。你在纸上多花十分钟算一遍Tq、采样点、SJW,能省掉现场两天拿CAN分析仪回放报文的时间。

调试时我还有个习惯:把被测节点先设成只听模式,只收不发,观察错误计数器。等错误计数器稳定为0后,再打开正常收发模式继续跑。这个小技巧能帮你快速把“配置问题”和“总线冲突问题”剥离开来,尤其是排查新节点接入已有总线的场景时,非常管用。CAN位时间配置这件事,七分靠公式,三分靠实测。烧程序前拿一张纸把公式推一遍,把Tq、采样点、SJW写清楚;烧完后用示波器抓波形、量位宽,再跑一轮长线压力测试。你在设计阶段多留一点余量,总比在后装现场拿示波器对着CAN_H和CAN_L发呆要舒服得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 10:59:25

SpringBoot+Vue+MyBatis+MySQL:Web及游戏管理后台搭建实战指南

做管理后台这件事,说难不难,说简单也真不简单。尤其像标题里这种“Web及游戏管理平台管理系统”,一听就知道既要管用户、管内容,又要管游戏服务器的运营状态和玩家数据,涉及的模块相当杂。我前后接过好几个类似的项目&…

作者头像 李华
网站建设 2026/10/5 10:59:20

登录记录全解析:从系统日志到异常登录排查实战

你按下电源键,输入密码,回车,屏幕亮起。整个过程看起来平淡无奇,但系统从你按下回车那一刻就开始记账了:这个登录动作发生在什么时间、用的是哪个账户、通过什么方式认证、从哪台设备连进来的、最终成功还是失败——这…

作者头像 李华
网站建设 2026/10/5 10:58:01

YOLOv11多摄像头协同追踪与异常检测实战指南

简介:本资源是一份面向安防系统工程师、计算机视觉开发者及智能监控项目实践者的深度技术文档,聚焦YOLOv11在多摄像头协同追踪与异常事件检测中的工程落地。文档共36页PDF,结构完整、支持目录跳转与左侧大纲导航,涵盖引言、YOLOv1…

作者头像 李华
网站建设 2026/10/5 10:55:53

Redis六层学习路径:从基础命令到源码剖析的进阶指南

1. 先说清楚:这套学习路径到底要解决什么问题我在很多技术群里见过一种典型现象:有人拿Redis当缓存用得挺溜,set/get倒背如流,一聊到生产环境就露馅。主从延迟怎么处理?缓存和数据库不一致了怎么办?集群扩容…

作者头像 李华
网站建设 2026/10/5 10:55:34

DevEco Studio鸿蒙开发全流程实战:从环境搭建到应用上架

1. 从零认识DevEco Studio:鸿蒙开发的核心工作台1.1 为什么大家都绕不开DevEco Studio做鸿蒙开发,第一道门槛就是开发工具。很多人拿到华为老手机想折腾、想自己写点小应用时,第一反应是去搜“鸿蒙开发用什么软件”,搜出来的答案几…

作者头像 李华