拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

STM32CubeMX从入门到实战:配置SPI驱动W25Q64与FreeRTOS集成

STM32CubeMX从入门到实战:配置SPI驱动W25Q64与FreeRTOS集成

1. 为什么STM32CubeMX值得花时间折腾

第一次接触STM32的人,十有八九是从寄存器或者标准外设库开始的。翻着几百页的参考手册,对着RCC->APB2ENR这种寄存器一位一位地查,好不容易点亮一个LED,结果换个芯片型号,所有代码推倒重来。这种体验劝退了太多人。STM32CubeMX就是ST官方为了终结这种痛苦而推出的图形化配置工具,它把芯片选型、时钟树配置、外设初始化、中间件集成、代码生成这一整条链路全部可视化,你点几下鼠标,它就把底层初始化代码给你生成好,你只需要在指定的用户代码区写业务逻辑。

这个工具能做的事情比很多人想象的多。它不只是生成几个初始化函数那么简单,它内置了引脚冲突检测、时钟树自动求解、功耗估算、外设参数校验,还能直接集成FreeRTOS、FatFs、LwIP、USB Device这些中间件。对于用HAL库做硬件SPI读写W25Q64这类SPI Flash芯片的场景,CubeMX能帮你把SPI的时钟极性、相位、分频系数、数据宽度全部配好,生成的代码直接就能用,省去了大量查手册和调试的时间。

这篇文章适合谁看?如果你是刚入门的嵌入式学习者,手上有一块STM32开发板但不知道怎么开始,这篇教程能带你从零走完下载、安装、配置、生成代码、编译下载的完整流程。如果你是从标准库转过来的老手,想了解CubeMX+HAL这套组合的实际使用体验和踩坑点,文章里关于时钟树配置、代码生成选项、用户代码保护这些细节同样对你有用。如果你正在做W25Q64这类SPI Flash的读写项目,我会在实操部分专门讲SPI接口的配置要点和常见问题排查。

注意:STM32CubeMX的版本迭代比较快,不同版本界面布局和选项位置可能有差异。本文基于当前主流版本的操作逻辑来写,如果你用的版本较老或较新,遇到界面不一致的地方,优先看选项名称而不是位置。

2. 下载前的准备工作与版本选择

2.1 确认你的芯片系列和开发环境

在下载CubeMX之前,先想清楚两件事:你用的是哪个系列的STM32,以及你打算用哪个IDE写代码。这两个问题的答案会直接影响你后续的安装配置和固件包下载。

STM32的系列非常多,从低端的F0、G0,到主流的F1、F4,再到高性能的H7、MP1,每个系列对应的固件包(Firmware Package)是独立的。CubeMX本身是一个Java写的桌面应用,安装包不大,但它依赖的HAL库固件包每个系列动辄几百MB。如果你不确定自己用哪个系列,可以看芯片表面的丝印,比如STM32F103C8T6就是F1系列,STM32F407VET6就是F4系列。

IDE方面,CubeMX支持生成多种工程格式:STM32CubeIDE(ST自家的免费IDE,基于Eclipse)、MDK-ARM(Keil,国内用得最多)、IAR EWARM、Makefile、CMake等。如果你用Keil,生成工程时选MDK-ARM,版本号根据你装的Keil版本选,一般选V5就行。如果你还没装IDE,建议直接用STM32CubeIDE,它和CubeMX的集成度最高,安装也最省事。

2.2 下载渠道与安装包选择

STM32CubeMX的官方下载渠道是ST的官网。直接在搜索引擎里搜“STM32CubeMX download”,找到ST官网的页面进去。页面上会有多个版本的安装包,Windows平台一般提供.exe安装程序和.zip免安装包两种。我建议下载.exe安装程序,它会自动处理一些依赖和快捷方式,省得手动配置。

下载之前需要注册一个ST账号,这个账号是免费的,用邮箱注册就行。注册过程中会让你填一些基本信息,如实填就好。登录之后就能看到下载链接了。有时候官网的下载速度不太稳定,如果遇到下载中断,可以换个时间段再试,或者用浏览器的断点续传功能。

安装包的大小通常在几百MB左右,具体取决于版本。下载完成后,Windows下双击.exe文件开始安装。安装过程中会让你选择安装路径,默认路径在C盘用户目录下,如果你的C盘空间紧张,可以改到其他盘。安装程序还会问你是否安装ST-Link驱动,如果你用ST-Link下载器,这个一定要勾上。

提示:安装路径里尽量不要有中文和空格,虽然新版本对中文路径的兼容性好了很多,但为了避免一些莫名其妙的报错,用纯英文路径最稳妥。

2.3 安装过程中的选项说明

安装向导走到“Choose Components”这一步时,会列出几个可选的组件。除了主程序之外,通常还有“ST-Link Upgrade”、“STM32CubeProgrammer”等。ST-Link Upgrade是用来升级ST-Link固件的,建议勾上,有时候下载器固件太老会导致连接问题。STM32CubeProgrammer是一个独立的烧录工具,如果你用CubeIDE或者Keil自带的烧录功能,这个可以不装,但装了也没坏处,它支持多种下载方式,偶尔能救急。

安装完成后第一次启动CubeMX,它会让你选择一个工作空间目录(Workspace),这个目录用来存放你创建的工程文件和下载的固件包。默认在用户目录下的STM32CubeMX文件夹里。如果你的工程比较多,建议单独建一个目录,比如D:\STM32Projects,方便管理。

启动后还会弹出一个“Firmware Package”的下载提示,问你是否要现在下载固件包。如果你已经确定用哪个系列,可以在这里直接选对应的系列下载。如果不确定,可以先跳过,等创建工程时再下载。固件包的下载源默认是ST的服务器,国内下载速度可能比较慢,如果实在等不及,可以找找国内的镜像源,但要注意版本匹配问题。

3. 界面功能拆解与核心配置逻辑

3.1 主界面布局与工程创建入口

CubeMX的主界面分成几个区域:顶部是菜单栏和工具栏,左边是芯片选型和外设配置的入口,中间是芯片引脚图和配置面板,右边是代码生成和工程管理。第一次打开会觉得信息量很大,但其实常用的功能就那么几个。

创建新工程有两种方式:一种是“New Project”,从芯片型号开始选;另一种是“Board Selector”,从官方开发板开始选。如果你用的是官方Nucleo或Discovery板,用Board Selector会更方便,它会自动帮你配好板载的外设。如果你用的是自己画的板子或者第三方开发板,就用New Project,在搜索框里输入你的芯片型号,比如STM32F103C8,下面会列出匹配的芯片,双击选中即可。

选中芯片后会进入主配置界面。左边一列是外设分类:System Core、Analog、Timers、Connectivity、Computing、Middleware。每个分类下面展开就是具体的外设。中间是芯片的引脚分布图,绿色的引脚表示已经被配置了功能,灰色的表示未使用。你可以直接在引脚图上点击某个引脚来分配功能,也可以在左边的外设列表里配置。

3.2 时钟树配置的核心逻辑

时钟树配置是CubeMX里最核心也最容易出错的部分。STM32的时钟源有四种:HSI(内部高速时钟)、HSE(外部高速时钟)、LSI(内部低速时钟)、LSE(外部低速时钟)。HSI和LSI是芯片自带的,不需要外部元件,但精度较差。HSE和LSE需要外接晶振,精度高,适合对时序要求严格的外设,比如USB、以太网、RTC。

配置时钟树时,先确定你的板子上有没有外部晶振。如果有8MHz的HSE晶振,就在RCC配置里把HSE设为Crystal/Ceramic Resonator。然后在Clock Configuration标签页里,把PLL Source选为HSE,接着配置PLL的倍频和分频系数,让系统时钟(SYSCLK)达到你想要的频率。比如F103系列最高可以跑到72MHz,你可以把HSE 8MHz先除以1,再乘以9,得到72MHz。

CubeMX的时钟树界面有一个很实用的功能:如果你手动配置的时钟频率超出了芯片允许的范围,它会用红色标出冲突的节点,并在下方给出提示。你还可以点击“Resolve Clock Issues”按钮,让工具自动帮你求解一组合法的时钟配置。这个功能在你不确定怎么分频倍频的时候特别有用。

注意:USB外设对时钟精度要求很高,通常需要48MHz的时钟。如果你要用USB,在配置时钟树时要确保USB时钟是48MHz,否则USB可能无法正常枚举。CubeMX会在你启用USB外设时自动检查这一点。

3.3 外设配置的参数含义

以SPI接口为例,配置SPI时要关注几个关键参数:Mode(全双工/半双工/只接收/只发送)、Data Size(8位或16位)、Clock Polarity(CPOL)和Clock Phase(CPHA)、Prescaler(分频系数)、First Bit(MSB或LSB先行)。

CPOL和CPHA的组合决定了SPI的工作模式,共四种模式。W25Q64这类SPI Flash芯片通常支持Mode 0(CPOL=0,CPHA=0)和Mode 3(CPOL=1,CPHA=1)。具体用哪个模式要看芯片手册里的时序图。如果你不确定,可以先试Mode 0,大部分Flash芯片都支持。

分频系数决定了SPI的时钟频率。STM32的SPI时钟来源于APB总线,比如APB2跑72MHz,SPI1也挂在APB2上,那么SPI的基准时钟就是72MHz。分频系数选8的话,SPI时钟就是9MHz。W25Q64最高支持80MHz的SPI时钟,所以9MHz完全没问题。但如果你用的杜邦线连接,线比较长,频率太高可能导致通信不稳定,这时候可以适当降低频率。

NSS引脚的管理方式也要注意。如果你用软件控制片选,就把NSS设为Disable,然后在代码里手动拉低拉高对应的GPIO。如果用硬件NSS,就设为Hardware NSS。对于W25Q64这种单主单从的场景,软件控制片选更灵活,推荐用这种方式。

4. 从零开始:完整实操流程

4.1 创建工程并配置系统基础参数

打开CubeMX,点击“New Project”,在搜索框输入STM32F103C8,选中列表里的STM32F103C8Tx,点击右上角的“Start Project”。工程创建好后,先配置System Core里的几个基础项。

在SYS配置里,Debug选项选“Serial Wire”。这个选项决定了调试接口的引脚分配。如果你不选,CubeMX可能会把SWD引脚复用成普通GPIO,导致你下载一次程序后就再也连不上下载器了。这个坑我踩过,当时以为是下载器坏了,折腾了半天才发现是引脚被占用了。

在RCC配置里,把HSE设为“Crystal/Ceramic Resonator”,LSE根据你的板子情况设置。如果你的板子上没有32.768kHz的RTC晶振,LSE就保持Disable。

接着进入Clock Configuration标签页,配置时钟树。假设你的板子有8MHz的HSE晶振,目标系统时钟72MHz。操作步骤是:PLL Source选HSE,HSE分频选/1,PLL倍频选x9,系统时钟源选PLLCLK。这样SYSCLK就是8MHz除以1再乘以9等于72MHz。APB1分频选/2,APB2分频选/1,这样APB1是36MHz,APB2是72MHz。

4.2 配置SPI接口连接W25Q64

假设我们用SPI1来连接W25Q64。在Connectivity分类下找到SPI1,Mode选“Full-Duplex Master”,Hardware NSS Signal选“Disable”。然后在下面的Parameter Settings里配置参数:Frame Format选Motorola,Data Size选8 Bits,First Bit选MSB First,Prescaler选8,Clock Polarity选Low,Clock Phase选1 Edge。这样配置出来的是SPI Mode 0,时钟频率是72MHz除以8等于9MHz。

配置完SPI后,去引脚图里看看SPI1的引脚分配到了哪些GPIO上。F103C8T6的SPI1默认是PA5(SCK)、PA6(MISO)、PA7(MOSI)。片选引脚需要单独分配一个GPIO,比如用PA4,在引脚图上点击PA4,选择GPIO_Output,然后在GPIO配置里把它的初始电平设为High,模式设为Output Push Pull,速度设为High。

W25Q64的HOLD和WP引脚如果不用的话,可以接高电平,或者在代码里控制。为了简化,我们先把这两个引脚接VCC,不通过MCU控制。

4.3 配置FreeRTOS(可选但推荐)

如果你打算在项目里用FreeRTOS,可以在Middleware分类下找到FREERTOS,Mode选“CMSIS_V1”或“CMSIS_V2”。CMSIS_V2是较新的版本,支持更多的特性,但如果你用的HAL库版本较老,可能只有V1可选。选好之后,下面的Configuration里可以配置任务、队列、信号量等。

对于W25Q64读写这种场景,通常不需要复杂的任务调度,但如果你要在读写Flash的同时处理其他事情,比如串口接收、按键扫描,用FreeRTOS会让代码结构更清晰。你可以创建一个Flash读写任务,一个串口处理任务,通过队列传递数据。

配置FreeRTOS时要注意堆栈大小的设置。默认的堆栈大小可能不够用,特别是如果你在任务里调用了HAL库的函数,这些函数会占用不少栈空间。建议把每个任务的栈大小设为128字(512字节)以上,具体看你的函数调用深度。

4.4 工程管理与代码生成设置

在Project Manager标签页里,给工程起个名字,选好存储路径。Toolchain/IDE选MDK-ARM V5(如果你用Keil)或者STM32CubeIDE。在Code Generator里,有几个选项需要特别注意。

第一个是“Copy only necessary library files”,这个选项会让生成的工程只包含用到的库文件,工程体积小,但如果你后续要添加新的外设,需要重新生成。第二个是“Generate peripheral initialization as a pair of .c/.h files”,这个选项会把每个外设的初始化代码单独放在一对文件中,代码结构更清晰,推荐勾上。

最重要的是“Keep User Code when re-generating”这个选项。勾上之后,你在/* USER CODE BEGIN */和/* USER CODE END */之间写的代码在重新生成时不会被覆盖。这个机制是CubeMX的核心保护措施,一定要理解它的工作方式。你所有的业务逻辑都应该写在这两个标记之间,标记之外的代码在重新生成时会被清掉。

提示:即使有用户代码保护,重新生成前也建议先备份一下工程,或者用Git做版本管理。我遇到过几次因为改了配置导致用户代码区域被意外覆盖的情况,虽然不常见,但一旦发生就很麻烦。

4.5 生成代码并在Keil中编译下载

点击“GENERATE CODE”按钮,CubeMX会生成完整的工程文件。如果你选的是MDK-ARM,它会生成.uvprojx文件,双击就能用Keil打开。打开后先编译一下,确认没有错误。第一次编译可能会比较慢,因为要编译整个HAL库。

编译通过后,连接ST-Link下载器,在Keil里点击下载按钮。如果下载失败,检查一下Debug设置里的下载器型号是否选对,以及SWD接口是否连接正常。有时候需要按住复位键再点击下载,松开复位键后程序才能正常烧录。

下载成功后,程序就开始运行了。你可以写一个简单的测试代码,比如在main函数的while循环里翻转一个LED,确认程序在跑。然后就可以开始写W25Q64的读写代码了。

5. W25Q64读写实操与SPI调试要点

5.1 W25Q64的基本操作命令

W25Q64是一颗8MB的SPI Flash芯片,支持标准的SPI指令集。常用的命令有:写使能(0x06)、读状态寄存器(0x05)、页编程(0x02)、扇区擦除(0x20)、块擦除(0xD8)、芯片擦除(0xC7)、读数据(0x03)、快速读(0x0B)等。

写数据之前必须先擦除。W25Q64的最小擦除单位是扇区(4KB),擦除后该扇区的所有位都变成1。然后才能写入数据,写入的最小单位是页(256字节)。如果你要写的数据跨页了,需要分多次写入,每次写入前都要发写使能命令。

读数据相对简单,发读命令和地址,然后连续读取即可。W25Q64支持最高80MHz的读时钟,但标准读命令(0x03)的最高频率是50MHz左右,快速读命令(0x0B)才能跑到80MHz。我们用9MHz的SPI时钟,用标准读命令就够了。

5.2 用HAL库函数实现读写

CubeMX生成的SPI初始化代码已经配好了SPI1的参数。我们需要自己封装几个函数:片选控制、写使能、等待忙、擦除扇区、写页、读数据。

片选控制很简单,就是拉低和拉高PA4。写使能就是发0x06命令。等待忙是循环读状态寄存器,直到bit0为0。擦除扇区是发0x20命令加24位地址。写页是发0x02命令加24位地址加数据。读数据是发0x03命令加24位地址,然后读取指定长度的数据。

HAL库提供了HAL_SPI_Transmit和HAL_SPI_Receive两个函数,分别用于发送和接收。对于需要同时收发的情况,可以用HAL_SPI_TransmitReceive。这些函数的超时参数建议设大一点,比如1000毫秒,避免因为Flash忙导致超时。

// 片选控制 void W25Q64_CS(uint8_t state) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, state ? GPIO_PIN_SET : GPIO_PIN_RESET); } // 写使能 void W25Q64_WriteEnable(void) { uint8_t cmd = 0x06; W25Q64_CS(0); HAL_SPI_Transmit(&hspi1, &cmd, 1, 1000); W25Q64_CS(1); } // 等待忙 void W25Q64_WaitBusy(void) { uint8_t cmd = 0x05; uint8_t status; do { W25Q64_CS(0); HAL_SPI_Transmit(&hspi1, &cmd, 1, 1000); HAL_SPI_Receive(&hspi1, &status, 1, 1000); W25Q64_CS(1); } while (status & 0x01); }

5.3 常见SPI通信问题排查

SPI通信不上,最常见的原因有几个。第一个是片选引脚没控制对,或者片选信号极性搞反了。W25Q64的片选是低电平有效,所以空闲时应该是高电平,操作时拉低。第二个是SPI模式不对,CPOL和CPHA的组合跟Flash芯片不匹配。第三个是时钟频率太高,或者接线太长导致信号质量差。

排查的时候,先用示波器或者逻辑分析仪看波形。如果没有这些工具,可以用一个简单的方法:把SPI时钟降到最低(分频系数选256),然后发一个读ID的命令(0x9F),看能不能读到正确的ID。W25Q64的ID是0xEF4017。如果能读到ID,说明SPI基本通信没问题,再逐步提高时钟频率。

还有一个容易忽略的问题是电源。W25Q64的工作电压是2.7V到3.6V,如果你的MCU是5V供电,SPI电平不匹配,可能导致通信失败。F103是3.3V供电,跟W25Q64匹配,但如果你用了电平转换电路,要确保转换方向正确。

注意:W25Q64的WP引脚和HOLD引脚如果悬空,可能会导致意外写保护或通信中断。建议把WP接VCC,HOLD也接VCC,或者通过GPIO控制并确保初始状态为高。

6. 汉化、FreeRTOS集成与进阶技巧

6.1 中文汉化的实际体验

CubeMX的界面语言可以在Help菜单里的“Updater Settings”或者“Preferences”里切换。新版本通常内置了中文语言包,你可以在设置里找到Language选项,选中文后重启软件即可。汉化后的界面对于不熟悉英文术语的初学者确实友好很多,但有些专业术语的翻译可能不太准确,比如“Prescaler”翻译成“预分频器”没问题,但“Clock Polarity”翻译成“时钟极性”有时候会让人困惑。

我的建议是,初期可以用中文快速上手,但遇到关键配置项时,还是对照英文原版确认一下含义。特别是时钟树和外设参数这些地方,一个翻译偏差可能导致配置错误。另外,网上大部分教程和资料都是基于英文界面的,如果你完全依赖中文界面,看别人的教程时可能会对不上。

6.2 FreeRTOS与HAL库的配合要点

在CubeMX里集成FreeRTOS后,生成的代码会包含FreeRTOS的初始化和任务创建。HAL库的HAL_Delay函数在FreeRTOS环境下会变成阻塞式延时,它会占用CPU资源。更好的做法是用osDelay函数,它会让出CPU给其他任务。

如果你在中断服务函数里调用FreeRTOS的API,要注意使用FromISR结尾的版本,比如xQueueSendFromISR。普通的xQueueSend在中断里调用可能导致系统崩溃。

还有一个常见问题是优先级配置。FreeRTOS的任务优先级和中断优先级是两套体系。中断优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断不能调用FreeRTOS的API。在CubeMX里配置NVIC时,要注意这一点。通常把SysTick和PendSV的优先级设为最低,其他外设中断根据实时性要求来定。

6.3 代码生成后的工程结构管理

CubeMX生成的工程结构比较清晰:Core目录下是main.c、stm32f1xx_it.c等核心文件,Drivers目录下是HAL库和CMSIS,Middlewares目录下是FreeRTOS等中间件。你的业务代码应该放在Core/Src和Core/Inc下,或者单独建一个App目录。

如果你要添加自己的模块,比如W25Q64的驱动,建议在Core/Src下新建w25q64.c,在Core/Inc下新建w25q64.h。然后在CubeMX的Project Manager里,把这些文件添加到工程中。这样重新生成代码时,你的文件不会被删除,但需要在Keil里手动把文件加入编译列表。

提示:每次在CubeMX里改了配置重新生成代码后,打开Keil第一件事是重新编译一遍,确认没有因为文件变动导致的编译错误。有时候CubeMX会重新排列文件顺序,导致一些依赖关系出问题。

7. 我踩过的坑与实操心得

第一个坑是时钟配置错误导致下载后芯片“锁死”。有一次我把SWD引脚复用成了普通GPIO,结果下载一次程序后,ST-Link再也连不上芯片了。解决办法是按住复位键,点击下载,在松开复位键的瞬间程序开始烧录,抢在程序运行到引脚复用代码之前完成下载。或者用STM32CubeProgrammer的“Connect Under Reset”模式。这个教训让我养成了习惯:每次新建工程,第一件事就是配置SYS里的Debug为Serial Wire。

第二个坑是固件包版本不匹配。CubeMX生成的代码依赖特定版本的HAL库,如果你在Keil里手动更新了HAL库版本,可能会导致编译错误。我的做法是,除非有明确的需求,否则不轻易更新HAL库版本。如果必须更新,先在CubeMX里更新对应的固件包,然后重新生成代码。

第三个坑是SPI的NSS引脚配置。我一开始用硬件NSS,结果发现NSS引脚在每次传输后会自动拉高,导致连续传输时片选信号断断续续。后来改成软件控制NSS,问题解决。对于W25Q64这种需要连续传输的场景,软件控制片选是更可靠的选择。

第四个坑是FreeRTOS的堆栈溢出。我创建了一个任务,在里面调用了HAL_SPI_Transmit和HAL_SPI_Receive,任务栈只给了64字。结果运行一段时间后系统就死机了。后来把栈大小加到256字,问题消失。HAL库的函数调用层次比较深,栈空间要给足。

第五个坑是代码生成时用户代码被覆盖。有一次我在/* USER CODE BEGIN 2 */之外的地方加了几行代码,重新生成后全没了。从那以后,我严格遵守规则,所有代码都写在用户代码区域内。如果有些代码确实需要放在区域外,我会在重新生成后手动加回去,或者用条件编译的方式隔离。

提示:CubeMX的“Keep User Code”机制只保护特定注释标记之间的内容。如果你在标记之外修改了代码,重新生成时一定会被覆盖。建议在工程里加一个README文件,记录每次重新生成后需要手动恢复的改动。

8. 从CubeMX到实际项目的扩展思路

CubeMX生成的代码是一个起点,不是终点。在实际项目中,你需要在它的基础上做很多扩展。比如W25Q64的驱动,CubeMX只帮你配好了SPI接口,具体的读写逻辑、擦除算法、文件系统集成都需要自己实现。如果你要在Flash上跑FatFs文件系统,还需要配置FatFs中间件,实现磁盘读写接口。

另一个扩展方向是低功耗。CubeMX有功耗计算器,可以估算不同配置下的功耗。如果你做电池供电的设备,可以在CubeMX里配置低功耗模式,比如Stop模式或Standby模式,然后生成对应的代码框架。不过低功耗的调试比较麻烦,需要配合电流表实测。

还有一个实用的技巧是使用CubeMX的“Load/Save”功能。你可以把当前的配置保存为一个.ioc文件,下次直接加载这个文件就能恢复所有配置。这对于管理多个项目或者跟团队共享配置非常方便。.ioc文件是文本格式的,你也可以用Git来管理它的版本变化。

最后说一个关于学习路径的建议。CubeMX降低了STM32的入门门槛,但它不能替代对底层原理的理解。如果你只会在CubeMX里点鼠标,遇到复杂问题还是会卡住。我的建议是,用CubeMX快速搭建原型,同时花时间看HAL库的源码,理解它到底做了什么。比如HAL_SPI_Transmit函数,它最终操作的是SPI的DR寄存器,中间经过了哪些状态检查、超时处理,这些细节在调试时非常有用。等你对HAL库足够熟悉了,再回头看标准库甚至寄存器操作,会发现很多东西都是相通的。

返回列表