ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RT-Thread Studio与CubeMX联合编程实战:STM32外设初始化与RTOS整合指南

RT-Thread Studio与CubeMX联合编程实战:STM32外设初始化与RTOS整合指南 1. 为什么要把RT-Thread Studio和CubeMX凑到一起先说结论RT-Thread Studio是真的好用CubeMX也是真的香但这俩走在一起中间那几步缝缝补补的功夫十个人里有八个都踩过坑。我做嵌入式这几年光是切换工程工具、重配时钟树、手动搬初始化代码就浪费过不少时间后来撸清楚联合编程的完整链路之后效率明显上了一个台阶。那这里先聊聊两个工具各自的性格。RT-Thread Studio是RT-Thread官方出的一套IDE基于Eclipse魔改内置了RT-Thread内核、组件和大量软件包的下载、配置、编译、烧录、调试全流程对新手尤其友好开箱即用不用像以前自己搭工程那样纠结启动文件、链接脚本、库文件从哪来。CubeMX则是ST官方出品的图形化初始化代码生成器重点是可视化配置引脚、时钟树、外设参数一键生成HAL库工程。它最大的价值是让你不用反复翻寄存器手册一个下拉框搞定串口波特率、ADC采样时间、DMA通道映射这种基础而繁琐的活。把这两个工具结合起来本质上是希望各取所长用CubeMX来管理芯片底层和外设初始化用RT-Thread Studio来管理RTOS内核、驱动库和应用层代码。很多项目中驱动和外设配置会反复调整如果每次都回到纯手动模式既累又容易出错。联合编程的核心逻辑就是CubeMX负责板级初始化代码怎么生成RT-Thread Studio负责这些代码怎么组织、怎么和RTOS协同工作。适合看这篇内容的人我觉得大致有三类一类是刚接触RT-Thread手里又已经有一定CubeMX操作底子想探索两者协同的另一类是项目里用了RT-Thread但嫌BSP适配麻烦想直接用CubeMX快速改外设的还有一类是在裸机开发转RTOS过程中被各种初始化冲突和时间线问题折磨过的工程师。不管你是哪类把联合编程的那套编排思路捋顺了效率确实能提升不少。2. 联合编程的整体设计思路2.1 代码组织与目录结构设计先说一个非常重要的理念CubeMX和RT-Thread Studio本质上都在生成工程我们在联合编程时不能天真地认为RT-Thread Studio打开CubeMX工程就行也不能指望CtrlC、CtrlV就能解决所有问题。正确的方式是把代码划分为两个职责清晰的部分。CubeMX生成的部分我习惯叫它硬件抽象层工程它包含启动文件startup_*.sHAL库驱动源码Drivers/STM32F1xx_HAL_Driver大家几乎不会手动改系统初始化代码SystemClock_Config、MX_GPIO_Init、MX_USART2_UART_Init这类函数中断服务函数骨架stm32f1xx_it.c注意这里只做硬件中断的转发RT-Thread Studio负责的部分则是更上层的RTOS内核和组件线程、信号量、消息队列、finSH控制台驱动对接层把CubeMX生成的外设句柄挂到RT-Thread的设备框架上应用代码main线程、业务逻辑实际工程里我建议的目录结构是先在CubeMX里新建一个专门的工程目录然后在RT-Thread Studio里新建一个不带初始化的空项目再把CubeMX生成的核心代码目录整体复制过来。大家别嫌这一步麻烦目录结构理清楚后面所有坑的排查难度会小一个量级。我的习惯是app/ ├── board/ # RT-Thread板级支持包含board.c、board.h ├── applications/ # 应用代码main.c等 ├── CubeMX_Core/ # CubeMX生成的核心文件 │ ├── Inc/ # 头文件如main.h, usart.h, gpio.h │ └── Src/ # 源文件如main.c, usart.c, gpio.c ├── Drivers/ # HAL库源码可选择性保留 └── rt-thread/ # RT-Thread系统源码这套结构把CubeMX相关文件集中在CubeMX_Core里一眼就能看出来哪些是自动生成、可以重新生成的哪些是手动改过、不能随意覆盖的。2.2 核心集成方式CubeMX生成代码如何接入RT-Thread工程这一步是联合编程的关键也是最容易糊弄、后患最多的地方。如果你只是把main.c复制到RT-Thread工程里编一个试试大概率会满天飞报错。原因是CubeMX生成的main.c里自带了一个main()函数而RT-Thread的启动流程里也有一个main()入口函数两边必定要打架。所以你要做的第一个动作是关闭CubeMX生成main函数的选项。在CubeMX工程文件的Project Manager - Code Generator在Generated files区域把Generate peripheral initialization as a pair of .c/.h files per peripheral勾上再在Generate files部分取消勾选Generate main() function相关的选项。这样生成的代码每个外设单独一对.c/.h文件而且不会产生一个与自己冲突的main()函数。这一点在不同版本的CubeMX菜单文字略有差异但大方向是一致的。在RT-Thread Studio侧它通常从rt_hw_board_init()开始执行然后调用$Sub$$main这样的包装函数再进到初始化和调度。你要做的是在RT-Thread的初始化流程里找一个合适的时机调用CubeMX生成的外设初始化函数。最简单的做法是在board.c的rt_hw_board_init()里或者在main线程创建之前调用例如MX_GPIO_Init()、MX_USART2_UART_Init()这样的一堆初始化函数。这里有个细节CubeMX生成的初始化函数内部会用HAL_Init()、SystemClock_Config()来配置时钟。而RT-Thread的board.c里往往也已经有一套时钟配置SystemClock_Config()或rt_hw_clock_init()。这时候必须做取舍不能两套并存。我的建议是保留CubeMX的时钟配置把RT-Thread原board.c里的时钟初始化弱化成空操作或者直接删掉避免两个来源各配一遍导致某个外设跑到和预期不同的时钟频率上。2.3 必须避免的设计坑第一点不要去改HAL库源码。很多人在CubeMX生成的代码上绕着绕着发现问题出在某个HAL函数上上去就改HAL库内部逻辑。这是灾难的开始因为CubeMX一旦重新生成工程这些改动就会丢而且改库代码的意图你过三个月再看大概率连自己都看不懂了。正确做法是把对HAL库的修改封装成自己的模块或者靠重写回调函数、宏配置来满足需求。第二点不要在外设中断服务函数里写业务代码。CubeMX生成的stm32f1xx_it.c里各种中断处理函数都是现成的你会忍不住在串口中断里直接塞数据处理逻辑。但RTOS环境里中断处理讲究快进快出大量逻辑应该通过队列、信号量、二值事件等机制交给线程处理。建议中断里只做最小处理比如读数据寄存器、清标志位、发送同步对象真正的主业务放在RT-Thread线程里接收同步对象并处理。第三点回调和HAL使能要配套。比如你使能了串口接收中断那么在CubeMX里也要配置好NVIC优先级同时RT-Thread的中断嵌套开关要做好阈值设置。别小看这一条我在项目里遇到过两三次串口接收时不定期丢数据的问题查到最后都是NVIC优先级和FreeRTOS/RTT的临界区开关不匹配导致的。一般建议把HAL的中断优先级设置为RT-Thread支持的可延迟中断优先级范围内通常是5~15具体取决于LOWEST_INTERRUPT_PRIORITY等宏避免在临界区内处理慢中断导致系统崩溃。3. 完整实操从CubeMX配置到RT-Thread Studio运行3.1 CubeMX工程配置要点我会拿一个最常见的STM32F103C8T6小板子举例这个型号在淘宝上十块钱不到玩的人最多。先打开CubeMX新建一个基于MCU的工程选择STM32F103C8Tx。进入Pinout Configuration界面后做以下几件事配置外部高速晶振RCC选择Crystal/Ceramic Resonator方便后续把系统时钟从HSI切到HSE系统能获得准确的主频。在SYS中把Debug选项选为Serial Wire不然板载ST-Link调试会出问题下载一次之后第二次就连不上芯片了。按你的需求配置USART1或USART2我一般用USART1做日志输出波特率设置115200字长8位无校验1个停止位。在NVIC Setting选项卡中使能中断。配置一个LED的GPIO引脚比如PC13输出模式初始电平设置成High方便后面控制LED灭。时钟树页面把HSE设为外部晶振来源输入频率填8MHz看你的板子实际晶振然后倍频到最高72MHz。注意如果你想搞低功耗或特定的USB应用时钟树配置要更细致但普通测试项目直接拉满72MHz就行。在Project Manager页面工具栏里选择Project设置项目名和保存路径。重点来了在Toolchain/IDE那一栏你随便选哪个都行因为后面我们并不直接用它编译整个工程而是借用它的生成能力。我的习惯是选择MDK-ARM V5因为后续如果想单独调试CubeMX代码可以直接双击MDK工程。然后进入Code Generator区域把之前说的Generate peripheral initialization as a pair of .c/.h files per peripheral打勾把Generate main() function取消勾选。再回到Project Manager里确认一下Generated files里的配置点击GENERATE CODE生成代码。3.2 在RT-Thread Studio中创建基础工程并集成外部代码RT-Thread Studio先创建一个新的RT-Thread项目选择对应的芯片型号和BSP。如果你是STM32F103系列可以直接在BSP选择界面找到对应的开发板或评估板模板。BSP选好之后Studio会替你生成一个能跑的基础工程注意这个基础工程里本身已经集成了一套外设驱动和时钟初始化所以在整合之前先编译一次确保原始工程是无误的。然后打开你的CubeMX工程目录把以下内容复制到RT-Thread工程里Drivers/STM32F1xx_HAL_Driver目录如果RT-Thread BSP已经自带了类似目录就优先用BSP里的避免版本冲突。Inc和Src目录下的所有文件这里指的是CubeMX生成的外设初始化.c/.h及main.h等。startup_stm32f103xb.s启动文件如果RT-Thread BSP里已有可用的就用BSP里的。复制完了在RT-Thread Studio的项目资源管理器里选中文件夹按F5刷新新的文件会被自动纳入工程。此时去编译大概率会报错。为什么因为CubeMX生成的代码里会包含对HAL_MspInit()等函数的重定义而RT-Thread BSP的board.c里也有相关实现。解决办法是把RT-Thread BSP里自带的、和HAL初始化重复的部分注释掉或删除以CubeMX的为基准。如果你对代码diff不熟我的做法是直接在RT-Thread工程的board.c里把HAL_MspInit()和HAL_StatusTypeDef HAL_Init()这类的定义直接注释掉改用CubeMX生成的stm32f1xx_hal_msp.c文件。3.3 主程序衔接初始化顺序和线程设计搞定编译问题后接下来要理清楚初始化顺序。在RT-Thread的启动流程中rtthread_startup()会依次调用rt_hw_board_init()、rt_components_init()、然后创建应用main线程。所以我通常建议把CubeMX外设初始化放在rt_hw_board_init()里特别是在设置系统时钟之后、OS调度器启动之前。举一个实际例子。你可以在board.c里找到void rt_hw_board_init(void)在里面加入这样一段调用#include main.h #include usart.h #include gpio.h void rt_hw_board_init(void) { /* 初始化HAL库、配置时钟和全部外设 */ HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); /* 系统堆栈和动态内存堆初始化原有逻辑保留 */ ... }注意一点SystemClock_Config()不是RT-Thread BSP里那个版本而是CubeMX生成的版本。所以如果你原BSP里已经有一个同名函数需要把它全局屏蔽统一用CubeMX的。最好的办法是在RT-Thread里不保留原名函数避免符号冲突。串口初始化完成后还没完因为RT-Thread的串口驱动框架要使用这个USART有两种常用方式一种是把你的外设注册成RT-Thread标准设备在应用层调用rt_device_find()来读写另一种是直接在应用层调用HAL库函数比如HAL_UART_Transmit()发字符串不走RT-Thread设备框架。前期调试为了快速跑通我推荐先用HAL函数直接发数据确认硬件链路OK再改回标准设备模型这样分开排查问题更快。线程设计方面我建议开两个线程一个shell_thread用来处理控制台命令一个app_thread用来处理主营业务比如周期翻转LED、发送串口数据。创建线程时栈大小要统筹考虑HAL库和printf的底层实现会吃掉不少栈空间一般来说最小256字节起步实际测试时建议512或1024。如果你使用了RT-Thread的FinSH那Shell线程的栈也要加大一点。3.4 编译、下载与验证到这里联合编程工程已经基本成型。直接在RT-Thread Studio里编译如果顺利通过很多人会机智地在main线程里先加个LED翻转的裸循环编译下载看到LED在闪就说明CubeMX的时钟和GPIO配置已经生效RT-Thread内核调度也正常。验证串口的话在main线程里周期性调用HAL_UART_Transmit(huart1, (uint8_t*)Hello RT-Thread CubeMX\r\n, strlen(Hello RT-Thread CubeMX\r\n), 0xFFFF);然后用USB转TTL接上串口1的TX、RX和GND打开串口助手如果看到字符串输出说明联合编程最小系统打通。接下来再去做复杂外设ADC、SPI、I2C、DMA等的联合配置心里就有底了。提示下载如果遇到No target connected或连接不上先检查ST-Link驱动和接线另外确认CubeMX里SYS Debug开的是Serial Wire否则芯片很快会被锁住。真锁住了用STM32CubeProgrammer连接并使用低电平复位引脚erase整片即可。4. 核心参数与配置详解4.1 时钟树配置的注意事项时钟是整个芯片的生命线我对每个联合编程项目都强调时钟配置要优先确认。CubeMX的时钟树页面点几下就能配置好看似简单但有几个隐藏点PLL来源选择。很多人默认选择HSI内部高速时钟这样省了个外部晶振但USB等外设对时钟精度要求较高HSI误差会直接导致USB枚举不稳定。所以建议尽量用HSE。如果板子上的晶振频率不是8MHz记得在CubeMX的Frequency设置里填对比如12MHz晶振CubeMX会自动重新计算倍频和分频系数你不改的话得出的芯片主频会是108MHz甚至更高直接超频到不稳定状态。AHB/APB分频系数。在CubeMX里你只关心最终系统时钟是72MHz但APB1和APB2的分频值也要留意因为这会直接影响串口、定时器、ADC等外设的输入时钟。比如APB1最高36MHz如果设错了USART的波特率会算不对莫名其妙乱码。强烈建议在时钟树页面把AHP、APB1、APB2分频值都截图留档方便后面查问题。LSE外部低速时钟问题。如果你的板子没有焊接32.768kHz晶振就不要在RCC配置里开启LSE。我之前在一款国产小板子上发现打开LSE后系统启动经常卡在等待LSE就绪的死循环里排除了半天才发现晶振根本不存在。按CubeMX默认配置选择No或Disable就好。4.2 中断管理的两种模式和坑RT-Thread和裸机开发相比中断处理多了一个跟线程同步的问题。CubeMX在stm32f1xx_it.c里为你生成了中断处理函数比如USART1_IRQHandler()。这个函数默认会调用HAL_UART_IRQHandler(huart1)。如果你直接往里面塞数据处理就回到了裸机思维。联合编程里我推荐两套中断管理方式方式一中断中只发通知线程中做处理。以串口接收为例在串口中断里判断接收完成标志然后通过rt_sem_release()释放一个信号量或者rt_mq_send()发一条消息。有一个专门的接收线程在等这个信号量收到后调用HAL_UART_Receive()或直接从缓冲区读数据。这样可以确保协议解析、数据打包等耗时操作都在线程上下文中执行不会卡住整个系统。方式二完全交给RT-Thread的串口驱动框架。这需要你在CubeMX生成的HAL外设之上再对接RT-Thread的DMA接收和IDLE中断检测。这种方式更标准但配置量也更多。做法是在RT-Thread的串口设备驱动里使用rt_device_register把你初始化好的huart挂到/dev/uart1上上层直接用open/read/write访问。前期调通联合编程我不建议一上来就选这种等整体流程稳固后再进阶。中断优先级这块常见的坑是优先级分组设置前后不一致。CubeMX在HAL_Init里会设置HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2)之类而RT-Thread的中断配置也有自己的分组和临界区嵌套方式。如果两边不一致最典型的问题是外部中断处理里用rt_kprintf会死机或者响应忽快忽慢。我的调整方法是以CubeMX为主在HAL_Init()之后明确调用一次HAL_NVIC_SetPriorityGrouping()并在RT-Thread的board代码里不重复配置而是用同一个分组。4.3 HAL库与RT-Thread的互斥、延时、打印冲突这个环节是联合编程中被吐槽最多的。裸机代码里你可以在中断里调用HAL库函数可以随便用HAL_Delay()延时。但在RT-Thread环境里这些操作都有可能成为系统崩溃的导火索。先聊延时。HAL_Delay()内部是一个自旋等待SysTick中断抹平时间。但你一旦启动RTOS调度器SysTick已经被RT-Thread接管了HAL_Delay()里的uwTick不再更新程序会死等在延时循环里。所以在RT-Thread线程里不要用HAL_Delay()你必须替换为rt_thread_mdelay()这个函数在延时的同时会让出CPU让其他线程先跑。接着说打印。如果你在CubeMX外设上配好了UART再调用printf()默认走的是C库的fputc或_write它不会让出CPU也不一定线程安全。在RT-Thread中常见的做法是重映射rt_kprintf输出到一个串口驱动或者用RT-Thread的rt_device接口在应用层封装一个rt_console。我的做法是调试阶段直接用HAL_UART_Transmit()发送字符串不碰C库printf等稳定之后再把输出接入日志组件。至于互斥HAL库本身没有线程安全的概念比如在多个线程同时调用同一个外设的HAL函数就会出问题。这时要给每个外设加一把RT-Thread互斥锁或者是单线程访问限制只有一个线程操作该外设。这块虽然不复杂但特别考验收尾的严谨程度。4.4 低功耗、DMA等高级外设的联合配置低功耗模式在RT-Thread和CubeMX联合编程里算是一个进阶却容易绕弯的题目。CubeMX可以把低功耗唤醒源配置得非常细比如RTC唤醒、外部中断唤醒、串口IDLE唤醒等。但RT-Thread有自己的电源管理组件PM组件如果两块配置都启用可能会出现低功耗进入了可唤醒源却什么都没配或者唤醒之后系统直接跑飞的诡异现象。我的建议是联合编程的前期先不接入RT-Thread PM组件而是用CubeMX直接配置某个外设中断来唤醒同时把整个系统卡在WFI或STOP模式的逻辑放进一个专门的线程里跑通之后再去平滑替换成PM组件。这样拆开来排错会快很多。DMA的坑主要在缓冲区分配和Cache一致性上。如果是STM32F1这类没有Cache的Cortex-M3还好但像F4、H7这些带Cache的芯片就必须注意DMA缓冲区的对齐和Cache Invalidate/InfoClean操作。RT-Thread提供的公共内存分配函数默认对齐粒度不一定满足DMA要求如果你要使用DMA做串口或ADC循环采样建议用rt_malloc后手动对齐或者直接定义一个全局静态数组并加__attribute__((aligned(32)))来保证缓冲区地址对齐。5. 常见问题与排查技巧实录5.1 编译错误类问题问题1CubeMX代码里报错undefined reference to HAL_MspInit。这个错误本质上是找不到HAL底层模块的MSP初始化函数。原因一般是RT-Thread BSP原本处理MSP的方式和CubeMX生成文件里的方式起了冲突或者是MSP实现文件没被加入工程。排查步骤确认stm32f1xx_hal_msp.c被复制进了工程的Src目录并且被编译。搜索整个工程看是否有两个HAL_MspInit()定义。若有两个把RT-Thread里的那个注释掉保留CubeMX的即可。问题2报错multiple definition of main。这个错最直接的原因就是CubeMX生成的文件里带了一个main函数而RT-Thread也有main函数的入口。解决办法是回到CubeMX的Code Generator里取消生成main函数再重新生成一次然后用CubeMX新生成的Src覆盖旧的。问题3报错unknown type name UART_HandleTypeDef等外设类型未定义。一般是头文件包含路径没设置好RT-Thread Studio的工程里需要手动添加CubeMX的Inc目录和HAL驱动Drivers目录到C/C General - Paths and Symbols里。注意加入路径后最好先Clean一下工程再重新Build否则IDE有时不会重新扫描头文件报错依然存在。5.2 运行异常类问题问题1程序运行到HAL_Delay()就卡死。这个我之前已经提到过在RTOS里千万别用HAL_Delay()它的uwTick由SysTick中断更新而SysTick已经归RT-Thread使用了。把所有HAL_Delay()替换成rt_thread_mdelay()卡死问题会立刻消失。问题2串口发送乱码。排查方向首先不是代码而是硬件连线。确认TX/RX接线别搞反波特率是否两边一致。如果硬件都正常再用示波器或逻辑分析仪打一下TXD电平看有无输出波形。软件层面重点检查时钟树里APB1或APB2分频尤其是USART的时钟来源。如果系统时钟从默认的8MHz改成72MHz之后波特率计算表如果没跟着变实际波特率会偏离设定值很大。最好在CubeMX时钟树里全局选定72MHz再看USART的波特率配置是否仍显示115200且不产生警告。问题3启动后SystemClock_Config里卡在while循环死等HSE。这需要先确认晶振是否起振。在CubeMX工程里将RCC HSE改为关闭使用HSI跑若代码能跑通说明晶振或晶振焊盘有问题。如果确认晶振没问题再检查RCC配置里的晶振源和型号是否匹配有些板子外置8MHz你却在CubeMX里配置外接高电平振荡器导致系统等不来HSE就绪。问题4RT-Thread调度器启动后外设中断不响应。优先检查NVIC优先级分组是否一致以及是否在初始化外设之前就启动了调度器。如果CubeMX的HAL初始化被放在rt_hw_board_init之后而RT-Thread在rt_hw_board_init里已经把调度器和底半区机制初始化了这时再初始化外设和NVIC可能会导致中断注册时序错乱。稳妥的顺序是在rt_hw_board_init中先做HAL_Init和SystemClock_Config再做外设Init最后再继续后续的BSP初始化。问题5加RT-Thread的驱动框架之后HAL库的中断回调函数没被调用。这是个非常典型的问题。CubeMX生成外设时如果配置了中断模式在HAL_UART_Receive_IT()之后HAL会在中断里调用回调函数HAL_UART_RxCpltCallback()。如果你在RT-Thread里也注册了串口驱动接收回调两者可能会互相覆盖或冲突。建议在同一时间只允许一种机制管理同一个外设的接收流程比如调试阶段直接用HAL接收业务稳定后再迁到RTT设备框架。5.3 实用调试技巧第一个技巧是善用RT-Thread的FinSH控制台。把串口1接到PC控制台组件启用后你可以用list_thread命令查看当前线程栈使用率list_device查看设备注册情况ps命令显示线程状态。这比单纯看串口打印多了很多信息排查死锁、栈溢出特别有用。第二个技巧是给CubeMX工程和RT-Thread工程各建一套调试配置。系统出现问题时先单独编译下载CubeMX生成的工程带main函数版确认纯裸机环境外设全部正常再切换到联合工程定位问题在哪一层。这个过程看起来很原始但确实能快速缩小问题范围比在一大坨代码里盲找容易得多。第三个技巧是给关键外设打时间戳。在重要代码路径中比如中断入口、线程切换、DMA完成回调处把当前系统时间记录到全局变量里系统跑飞后通过调试器查看这些时间戳变量能清晰还原出异常发生前后的调用序列。这招在处理偶发问题的时候比到处插打印语句高效得多。6. 写在最后的个人实操体会联合编程玩到一定程度你会发现真正的难点不在代码量而在上下文切换的心智成本。CubeMX里你会下意识用裸机思维处理外设RT-Thread Studio里又要时刻想着优先级、临界区、栈空间这些RTOS特有的东西。這兩套思维模式经常在同一个函数里碰撞所以我的建议是宁可多花半小时写清楚初始化顺序和线程职责也不要图快把所有东西塞进一个main文件里。拿我自己的项目来说一开始我为了快速验证结果把CubeMX生成的初始化代码全堆到rt_hw_board_init里把业务逻辑全塞进main线程的while循环结果功能确实能跑但一旦要加第二个外设就得反复改board.c耦合越来越严重。后来狠下心做了代码组织和接口封装花了一天时间重构才真正体会到联合编程的流畅感。从此之后我把自己的规则简化成三句话CubeMX管硬件实例和初始化RT-Thread管调度和设备框架应用代码只依赖设备和线程接口不直接碰寄存器。最后再分享一个小技巧CubeMX的工程文件尽量放在版本管理仓库里而RT-Thread Studio的工程也一起提交但两者各占一个目录互相不嵌套。这样哪天CubeMX配置要改外设重置代码提交记录非常清晰你随时能回退到上一版配置。别让IDE把工程文件和核心代码混在一起这比代码本身更像资产维护好它们你后续做任何芯片平台都会顺手很多。
返回列表