news 2026/9/7 2:18:55

嵌入式SPI协议实战避坑指南:时序、片选与多从机设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
嵌入式SPI协议实战避坑指南:时序、片选与多从机设计

搞嵌入式的没有不跟SPI打交道的,但恰恰是这个看起来最简单的协议,坑了我很久。直线接线没问题、逻辑分析仪上波形也有,屏就是不亮;换一个传感器模块,读出来的数据全是0xFF;把某个外设往总线上多挂一路,原来的设备直接罢工。这些问题我当年一个个都踩过,回头排查才知道根子上全是SPI的细节理解不到位。

这篇文章我把自己用SPI这几年踩过的坑、总结出的规律梳理一遍。从四根线到底怎么分工,到CPOL/CPHA时序模式怎么看数据手册,再到硬件片选和软件片选怎么选、一根总线挂多个从机要注意什么、硬件SPI不够用时怎么用GPIO模拟,最后把SPI和IIC、UART放在一起做个选型对比。内容全是实战里磨出来的经验,适合正在用STM32、ESP32、Arduino做项目,或者在调SPI屏幕、Flash、SD卡、各种传感器的朋友。

1. 先搞清楚SPI在解决什么问题,再去背四根线的名字

很多人学SPI,上来就背SCLK、MOSI、MISO、CS,背完就懵。我建议反过来想:SPI是被什么需求逼出来的。

1.1 为什么需要一种新的串行协议

早年的板级通信,UART是主力,但UART有个先天性缺陷——异步通信,收发两边各用各的时钟。你要发数据,就得提前约定波特率,两边时钟稍微有点偏差,数据就错位了。而且UART只能点对点,一台主机想同时跟多个外设通信,得接多路串口,管脚根本不够用。

后来IIC出现了,用两根线挂一堆设备,省管脚,还有地址机制。但IIC速度上不去,标准模式100kbps,快速模式也就400kbps,到了1Mbps已经属于高速模式,对时序要求还特别苛刻。我当年试图用IIC去读一颗200万像素的摄像头寄存器,光初始化配置就得小一秒,根本没法用。

在这个背景下,SPI就是冲着两个核心诉求去的:一是快,二是简单粗暴。它把时钟线单独拉出来,主机产生时钟、从机跟着时钟走,没有波特率对齐的问题,也没有地址帧、应答位的开销。时钟能拉到多高,取决于PCB走线和从机能跑多快。几十MHz非常常见。这就是SPI至今在中低速板级通信里不可替代的根本原因。

1.2 四根线各管什么事

SPI是主从结构,一个主机可以挂多个从机,最少只需要四根线,但注意——这个"最少"是有条件的,你挂的从机越多,片选线就越多。

  • SCLK:串行时钟,由主机产生,从机只能被动接收。时钟的频率就是数据传输速率的核心参数之一。
  • MOSI:Master Out Slave In,主机输出、从机输入。主机要发给从机的数据,通过这根线一位一位传过去。
  • MISO:Master In Slave Out,从机输出、主机输入。从机回给主机的数据走这根线。
  • CS/SS:片选信号,通常是低电平有效。主机把某个从机的CS拉低,等于告诉它"我要跟你说话",其他CS保持高电平的从机就闭嘴,MISO输出呈高阻态。

这里有个新手最容易忽略的点:SPI是全双工。MOSI和MISO是两根独立的线,主机发数据的同时,从机也在往MISO上放数据。所以一次SPI传输,读和写是同时完成的。这个特性直接影响后面对代码和时序的理解——很多人不理解为什么SPI读Flash要先发一个空字节,其实那个空字节就是时钟的"载体",没有MOSI上的数据,SCLK也得继续跑,从机才能把数据推出来。

1.3 主从结构带来的一个隐含约束

因为是主机负责产生时钟,所以SPI总线上的所有活动,必须由主机主动发起。从机没有时钟,它永远不会主动说话。这个特点既是优点也是限制:优点是好控制,主机想什么时候通信就什么时候通信;限制是如果某个从机有紧急事件需要通知主机,它只能靠另外一根中断线拉个引脚,SPI协议本身没有"从机主动上报"的机制。设计系统架构时,这一点要先想清楚,别等到做项目了才发现某个传感器需要实时上报,结果SPI总线这边还在轮询。

2. 时序模式是SPI一半的坑:CPOL、CPHA与数据手册该怎么读

说实话,SPI通信里我见过最多的问题,不是接线接错,而是时序模式对不上。主机和从机一个按模式0工作,一个按模式2工作,波形看起来都有,数据就是乱码。这一章我把这块彻底讲透。

2.1 从波形图看懂CPOL和CPHA

SPI的时序模式由两个参数决定:CPOL(Clock Polarity,时钟极性)和CPHA(Clock Phase,时钟相位)。

CPOL决定的是SCLK的空闲电平——就是不传数据的时候,时钟线停在什么电平。CPOL=0,空闲低电平;CPOL=1,空闲高电平。

CPHA决定的是数据在哪个边沿被采样。CPHA=0,在第一个边沿采样;CPHA=1,在第二个边沿采样。这里"第一个边沿"和"第二个边沿"要结合CPOL来理解:如果CPOL=0,时钟空闲是低电平,那第一个有效边沿就是上升沿;如果CPOL=1,时钟空闲是高电平,第一个有效边沿反而是下降沿。

两个参数一组合,就是四种模式:

模式CPOLCPHA空闲电平第一个边沿采样边沿
Mode 000低电平上升沿上升沿
Mode 101低电平上升沿下降沿
Mode 210高电平下降沿下降沿
Mode 311高电平下降沿上升沿

2.2 绝大多数设备用的都是Mode 0或Mode 3

实战里最常用的就是Mode 0(CPOL=0, CPHA=0),其次Mode 3(CPOL=1, CPHA=1)。为什么是这两个?因为SPI协议规范里对默认状态推荐的是极性和相位都为0或都为1,这两种模式足够覆盖绝大多数Flash芯片、SD卡、屏幕驱动、传感器模块。

Mode 1和Mode 2不是没有,但少见得多。所以如果你手里有一颗不认识的芯片,又没有数据手册,先试Mode 0,不行就试Mode 3,大概率能通。

2.3 数据手册里怎么定位时序模式

很多数据手册不会直接写"Mode 0"这几个字,需要你自己看图。这里我教一个很实用的方法:找到手册里的SPI时序图(SPI Timing Diagram),看三样东西。

第一,看SCLK空闲时是低还是高,就能确定CPOL。第二,看数据的稳定区出现在第一个边沿还是第二个边沿附近,如果数据在上升沿处稳定、下降沿处切换,说明是在上升沿采样,CPHA=0;反之则是CPHA=1。第三,很多手册还会给一张表格,里面有CPOL和CPHA或对应模式的说明,直接对照就行。

我见过最坑的一颗芯片是某国产陀螺仪,手册翻译得稀碎,时序图画成了Mode 1,结果实际是Mode 3,折腾了我半天。后来我学聪明了:直接用逻辑分析仪抓从机在已知频率下回的数据,换个模式试,哪种能解出正确ID就锁定哪种。

提示:买模块时优先选明确标注了SPI模式的成品模块,那种连时序模式都不标的模块,要么自己拿逻辑分析仪测,要么换一家买,别浪费项目时间。但如果必须用,可以按"先Mode 0后Mode 3、再Mode 2最后Mode 1"的顺序试,最快定位。

3. 片选决定成败:硬件NSS与GPIO软件片选怎么选

片选是SPI里最容易被低估的信号。很多人以为就是把CS拉低再拉高,结果遇到了各种莫名其妙的问界。

3.1 STM32硬件NSS的坑

STM32的SPI外设提供了硬件NSS功能:一种是NSS引脚由硬件自动控制,发送数据前自动拉低、发完后自动拉高;另一种是NSS输出模式,外设自己控制引脚状态。听起来很方便对吧?我当年也是这么想的,直到踩了坑。

坑在哪儿?硬件NSS的自动拉低/拉高时机,不是你想的那样干净利落。它在SPI传输完成但配置未清除时,可能会提前释放片选,导致从机认为本次传输结束,后面补的字节被当成新的一帧开头;而且它跟DMA、FIFO配合时行为更复杂。多层板子、高频时钟、长距离飞线,这些情况下硬件NSS很容易出诡异问题。

另外一个坑是引脚复用功能冲突。STM32的NSS脚经常和别的功能复用,你在CubeMX里开了硬件NSS,可能就把某个用得好好的GPIO或者别的外设引脚给顶掉了。

3.2 GPIO软件片选:简单、可控、推荐

我的建议是:除非有特殊需求,否则用普通GPIO做软件片选。逻辑上就是传输前把对应引脚拉低,传输完成后拉高。

// 软件片选控制示例(HAL库风格) #define CS_LOW() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET) #define CS_HIGH() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET) // 读Flash ID uint8_t spi_read_flash_id(void) { uint8_t cmd = 0x9F; // JEDEC ID命令 uint8_t id = 0x00; CS_LOW(); HAL_SPI_TransmitReceive(&hspi1, &cmd, &id, 1, 100); CS_HIGH(); return id; }

软件片选的好处有三个。第一,时机完全可控,你想在什么时候拉低、什么时候拉高,自己说了算,不会受外设状态机影响。第二,不占用专用NSS引脚,随便找个GPIO都能当片选,布局布线自由度大。第三,挂多个从机时,每个从机一个GPIO,逻辑一清二楚。

要注意的是,两个从机之间切换片选时,最好加一点间隔(比如几个微秒),不要一个CS刚拉高立刻拉低另一个。从机芯片内部需要时间完成当前帧的收尾,太快切换可能导致残留数据错位。我之前调一块屏幕和一颗Flash共用总线时,切换太快就会出现Flash偶尔读错地址的现象。

3.3 多从机场景的接线策略

一根SPI总线上挂多个从机,所有从机的SCLK、MOSI并在一起,MISO也并在一起,片选各自独立。这里有几个关键点必须注意。

MISO线是所有从机共用的,但同一时刻只能有一个从机在驱动这根线。所以从机在不被选中时,MISO必须呈高阻态,不能死顶着输出。绝大多数SPI从机芯片都是这么设计的,但个别廉价模块会在CS拉高后还拖个尾巴。遇到这种情况,要么在MISO上加个串联电阻缓冲,要么给每个从机MISO串一个小的三态缓冲器(比如74LVC1G125),成本不高但能保平安。

另外SCLK和MOSI在切换从机时,尽量保持在同一状态。很多从机是在CS下降沿才真正检测时钟边沿,如果CS拉低时SCLK刚好是一个毛刺,可能产生误触发。

4. 从CubeMX到逻辑分析仪:跑通一次真实SPI传输

这一章我带你把一次完整的SPI通信走通,从STM32CubeMX配置开始,到代码实现,再到用逻辑分析仪验证波形。这套流程我每次做新板子都会完整走一遍,能省掉后面80%的调试时间。

4.1 CubeMX里的关键配置项

打开CubeMX,选择一个SPI外设,你会看到一堆配置项。每项怎么填,对应什么底层逻辑,下面这个表格捋清楚了:

配置项常见取值说明
ModeTransmit Only / Full-Duplex等根据外设需求选,读多写多选Full-Duplex
Prescaler4/8/16/32/64等分频系数,决定实际SCLK频率
Clock PolarityLow / High对应CPOL,看从机手册
Clock Phase1st Edge / 2nd Edge对应CPHA,看从机手册
Data Size8 Bit为主个别传感器用16位,必须匹配
First BitMSB First为主极少数从机要求LSB First
NSSSoftware按上一章建议,优先软件片选
CRCDisable极少用,后文说明

Prescaler的计算逻辑很容易被忽略。STM32的SPI时钟源来自APB外设时钟,不同型号、不同总线配置,APB时钟不一样。如果你用的主频是170MHz,APB2外设时钟可能也是170MHz,Prescaler填8,SPI时钟就是170/8,约21MHz。有些屏幕模块最高只支持20MHz,你填个4倍分频就是42MHz,就会概率性花屏。所以务必先算清楚实际频率,再对一下从机的最高时钟限制。

4.2 基础收发代码框架

CubeMX生成初始化代码后,最常用的是HAL库的阻塞式收发接口:

// 写操作:发送buffer、不接收数据 HAL_StatusTypeDef status; status = HAL_SPI_Transmit(&hspi1, tx_buffer, len, timeout); // 读操作:先发命令/地址,再接收数据 HAL_StatusTypeDef status; status = HAL_SPI_Receive(&hspi1, rx_buffer, len, timeout);

这个Receive接口内部其实也是全双工的,发送部分是空字节,不需要你手动处理。它的本质就是主机输出空数据产生时钟,从机在时钟驱动下把数据放到MISO上,主机把它采进来。

不过要注意,阻塞式收发在传输过程中会一直占用CPU,如果你的系统里还有其他实时任务(比如PID控制、WiFi协议栈),建议使用中断或DMA方式。中断方式用HAL_SPI_Transmit_IT,DMA方式用HAL_SPI_Transmit_DMA,本质逻辑一样,只是数据传输不占CPU。用DMA时记得开启SPI的DMA请求中断,否则你不知道什么时候传输完成。

4.3 逻辑分析仪验证波形

写代码从来不是终点,验证波形才是。我强烈建议每一个做嵌入式的人搞一个24MHz以上采样率的逻辑分析仪,二三十块钱已经够用,调试SPI的效率能翻倍。

接线很简单:把逻辑分析仪的CH0接SCLK、CH1接MOSI、CH2接MISO、CH3接CS,共地接好,然后用配套软件打开SPI协议解码器,设置CPOL和CPHA与你的配置一致,抓一次通信。

第一次抓波形,重点看三件事。第一,CS是不是在传输期间全程拉低,有没有中途抖动;第二,SCLK频率对不对,跟预想差多少;第三,MOSI上的数据和解码器解出来的数据,是否跟你的发送buffer一致。把这些确认好了,再去解码MISO上的回包,往往问题瞬间水落石出。

我遇到过最典型的一个问题:主板SPI走线太长,SCLK信号过冲,逻辑分析仪上显示频率正确,但屏就是偶尔白屏。后来在SCLK上串了一个33Ω电阻,问题直接消失,这就是信号完整性问题在SPI上的经典表现。高频走线时,串阻抑制振铃不是玄学,是必要的工程手段。

5. 一根总线上挂多个从机:屏幕与SD卡共享SPI的实战经验

ESP32、STM32这类主控的SPI外设数量是有限的,常常需要让一块屏幕和一张SD卡共用一根SPI总线。这个需求非常典型,但也非常容易出问题。

5.1 为什么两个器件能共享一根总线

本质原因就是前面讲的片选机制。SPI是主从之间点对点通信,但通过片选信号可以实现时分复用。同一时刻,只有一个从机的CS是低电平,其他从机的CS都是高电平,它们的MISO处于高阻态,不会影响主机的MISO采样。所以屏幕和SD卡共享SCLK、MOSI、MISO,各自用独立的CS脚,逻辑上完全可行。

我在做一个小型数据记录仪时,就是让ILI9341屏幕和MicroSD卡共用ESP32的VSPI总线。屏幕负责显示,SD卡负责存储。两个器件按时间片轮流占用总线,互不干扰,实测很稳。

5.2 共享总线时必须满足的三个条件

第一个条件:片选必须独立且软件可控。这个前面已经说过,硬件NSS在这种场景下基本没法用。第二个条件:两个从机的时序模式必须一致,或者至少都兼容同一个模式。屏幕和SD卡绝大多数都支持Mode 0或Mode 3,如果你选的屏幕只支持Mode 2,SD卡只支持Mode 0,那就要在两个器件切换时动态切换SPI的CPOL和CPHA,增加复杂度不说,还容易出错。第三个条件:MISO线必须在从机未选中时是高阻,否则两个从机同时驱动MISO,电平冲突直接烧引脚。

第三个条件不是所有模块都满足。我踩过一个坑:某TFT屏幕模块在CS拉高后,MISO依然被驱动为低电平,SD卡正常工作时,这根线被它拽住了,导致SD卡的数据读取随机失败。排查了很久才定位到,最后在屏的MISO上串了一个1kΩ电阻,问题解决。所以买模块前或者画板前,最好先看下模块原理图,确认MISO是否有三态控制。

5.3 共享总线时的速率与负载问题

屏幕和SD卡对时钟频率的要求通常不一样。屏幕为了刷新率高,能跑到20MHz甚至更高;SD卡就娇贵一些,虽然标称支持25MHz,但在飞线场景下跑到10MHz以上就容易出错。

我建议的做法是:把总线时钟设成两个器件都支持的较低值,然后优先保证SD卡的稳定性。屏幕刷新率不够,可以通过优化显示缓冲、只更新局部区域来弥补,而不是硬拉高SPI时钟。ESP32的SPI外设支持按每次传输设置不同的时钟频率,这个功能很实用——操作SD卡时用低速(比如10MHz),刷新屏幕时用高速(比如26MHz),两边需求都照顾到了。如果主控没有这种特性,简单粗暴地统一用较低频率反而是最稳的。

还有一个很多人不重视的问题——总线负载。SCLK和MOSI是共享的,走线分支多、器件引脚电容叠加,会让信号边沿变缓。SPI频率越高越明显。如果板子上挂了三个以上从机,SCLK上串个22-47Ω电阻做阻抗匹配非常重要,同时尽量让分支走线等长且短。

6. 硬件SPI不够用时的Plan B:IO口模拟三线SPI

不是所有场景都适合用硬件SPI。当你遇到资源不足、时序特殊、或者手头主控根本没有硬件SPI时,用GPIO模拟SPI是一个必须掌握的技能。

6.1 什么时候真的需要软件模拟

我知道很多人听到软件模拟SPI会觉得"低级",其实不是。真实场景里,软件模拟SPI反而是救命稻草。

一种典型场景是引脚不够。某些封装很小的MCU,比如TSSOP16、SOIC8里塞的芯片,硬件SPI模块虽然有,但引脚都在特定位置,跟你实际布局对不上。这时候用IO口模拟三线SPI,就能把SPI放在任意GPIO上,布线的自由度一下子大很多。另一种是外设过多:一颗STM32F103C8T6只有两个SPI,一块板子上挂了一颗屏幕、一块Flash、一个CAN控制器、一个编码器,硬件SPI完全不够分,剩下的设备就用软件模拟。还有一种特殊场景:某个从机的时序非常特殊,硬件SPI的模式、位宽、片选行为都满足不了它,只能靠GPIO一点一点抠时序。

6.2 模拟SPI的代码框架

软件模拟SPI的核心就是两个动作:拉高拉低SCLK模拟时钟,在合适的边沿往MOSI写数据或者从MISO读数据。以三线SPI(SCLK、MOSI、MISO)为例,一个简单的发送函数可以写成这样:

#define SCLK_HIGH() HAL_GPIO_WritePin(SCLK_GPIO_Port, SCLK_Pin, GPIO_PIN_SET) #define SCLK_LOW() HAL_GPIO_WritePin(SCLK_GPIO_Port, SCLK_Pin, GPIO_PIN_RESET) void spi_soft_write_byte(uint8_t data) { for (int i = 7; i >= 0; i--) { // MSB First if (data & (1 << i)) { MOSI_HIGH(); } else { MOSI_LOW(); } SCLK_HIGH(); // 上升沿,数据被采样 SCLK_LOW(); // 时钟恢复低电平 } }

这是Mode 0(空闲低、上升沿采样)的写法。如果是Mode 3(空闲高、上升沿采样),初始化时要把SCLK拉到高电平,数据变换后在SCLK下降沿前保持稳定,然后下降沿让从机采样。

读操作跟写操作的差别在于:主机不需要关心MOSI上的数据,只需要在采样边沿把MISO的电平读进来。

uint8_t spi_soft_read_byte(void) { uint8_t data = 0; for (int i = 7; i >= 0; i--) { data <<= 1; SCLK_HIGH(); // 上升沿,从机把数据推到MISO data |= HAL_GPIO_ReadPin(MISO_GPIO_Port, MISO_Pin); SCLK_LOW(); } return data; }

6.3 软件模拟的时序与性能权衡

软件模拟的最大瓶颈是速度。一次简单的GPIO翻转加判断循环,在72MHz的STM32F103上大概要花几十纳秒,一个字节8个位,一帧可能还要加片选操作,算下来整体速率能跑到几MHz已经不错了。对SPI Flash的读操作这种纯数据搬运场景,软件模拟确实偏慢,但对传感器寄存器配置、LCD初始化这类低频操作,完全够用。

延时怎么加也有讲究。有些器件对SCLK的最小高低电平宽度有要求,比如手册写了最小SCK周期是200ns,那你翻转时钟后最好加一小段延迟(用for循环空转或者__NOP()),否则连续翻转太快,从机跟不上,表现为偶发数据错乱。加多长?按手册的时序参数来,留50%以上余量最稳妥。

软件模拟还有一个容易被忽视的细节:中断关闭。模拟SPI传输过程中,如果来了一个中断,GPIO电平状态在中断返回后可能跟期望的不一致,导致某一比特翻转丢失。所以在传输关键帧(比如Flash页写入)期间,临时关闭中断或临界区保护是很有必要的,否则偶尔的乱码会让你怀疑人生。

7. 别再用"快"来区分SPI和IIC:三组选型对比

每次一聊SPI,就有人拿IIC和UART一起来问。我总结了一套自己的对比框架,用物理层差异倒推应用场景,比单纯背"SPI快、IIC慢、UART简单"要靠谱得多。

7.1 物理层差异决定一切

UART是异步串行,收发双方各管各的时钟,所以必须约定波特率才能通信。它只需要TX和RX两根线,全双工,但没有时钟线,也就没有主从同步的概念,只能点对点。只要波特率一致、空闲电平都对,数据就能传,这是它协议最简单的一面。

IIC是同步串行,但它只有两根线——SCL时钟和SDA数据。数据线是双向的,半双工,同一时刻只能一个方向传输。它用地址机制解决多设备共享问题,每次传输主机发一个7位或10位地址选出从机,然后再传数据。少了片选线,代价就是传输效率低、时序要求严苛。

SPI是最"奢侈"的:时钟、发、收、片选四根线起步,全双工、速度快、协议开销几乎没有。但它的地址能力为零,多个从机完全靠片选线区分,从机数量越多,占用的GPIO越多。

7.2 什么时候用谁:一张表说清楚

对比维度SPIIICUART
线数4根起步(SCLK/MOSI/MISO/CS)2根(SCL/SDA)2根(TX/RX)
通信方式同步+全双工同步+半双工异步+全双工
多从机能力靠片选,从机多占管脚靠地址,从机多不占管脚点对点,不支持
速率数十MHz级别标准100k/400k,快速1M,高速3.4M取决于波特率,常用115200bps
典型场景屏幕、Flash、SD卡、ADC/DAC高速采样传感器小数据量、RTC、EEPROM、温度计GPS模块、蓝牙模块、串口调试、工业设备
协议开销几乎没有,裸数据有地址帧、应答位,效率打折有起始位、停止位、校验位

UART虽然慢,但在设备互联、长线传输、无线模块对接上仍然是最方便的,因为不需要共享时钟,双方各收各的,收发天然解耦。

7.3 数据手册上的"指纹"帮你快速判断

拿到一颗未知芯片,怎么快速判断它该用哪个协议?我以前的经验是看引脚数量和名字。

如果引脚里有SCL和SDA,那就是IIC;如果有SCLK/SCK加MOSI/MISO加CS,那就是SPI;如果只有RX和TX,那就是UART。这三组名字就是协议最清晰的指纹,几乎不会混淆。稍微特殊一点的是有些传感器同时支持IIC和SPI,通常通过某个引脚的电平或地址引脚选择模式,看手册里"It is possible to select I2C or SPI mode by..."这样的描述就能定位。

还有一个小提示:IIC的SCL和SDA外部必须接上拉电阻,SPI一般不需要(从机内部可能自带弱上拉或者直接推挽驱动),UART的TX/RX之间注意电平匹配、不要直接接RS232的电平。这几个细节在新手阶段最容易翻车,接口一接错,波形抓都抓不出来。

SPI这个协议,说实话,核心概念就那么几个:时钟、相位、片选、全双工。但每个细节展开都有实际工程的坑。最终我自己养成的习惯是:无论多简单的外设,上电后第一件事就是拿逻辑分析仪抓波形确认时序模式;无论多赶的项目,片选切换之间都留足余量;无论多贵的器件,都要核对一下最高SCLK限制。希望大家看完这篇文章,能在SPI这条路上少走一些我当年走过的弯路。

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

《我看见的世界》:李飞飞与ImageNet引爆深度学习革命

简介&#xff1a;《我看见的世界》是知名AI学者李飞飞撰写的个人与科技交织之作&#xff0c;面向人工智能初学者、从业者以及对科技人文话题感兴趣的普通读者。书中以作者独特视角梳理AI的定义、发展历程与社会影响&#xff0c;并结合她作为终身学者赴美国众议院作证的里程碑经…

作者头像 李华
网站建设 2026/9/7 2:17:05

【信息科学与工程学】计算机科学与自动化——第一百五十九篇 前端领域中常见的核心算法与功能分类02

全部聚焦 CSS 领域,涵盖最新的 CSS 特性如三角函数、颜色函数、滚动驱动动画、视图过渡、文本平衡、形状、滤镜、混合模式、计数器、自定义属性、排版、网格高级、弹性盒子、滚动条、打印、分页、字体、变换、动画关键帧、过渡、环境变量、媒体查询、容器查询、层叠层、作用域…

作者头像 李华
网站建设 2026/9/7 2:16:49

WTL实战指南:从环境搭建到消息映射与控件封装的完整解析

简介&#xff1a;这是一份面向 Windows 桌面开发者的 WTL 教程合集与开发者指南&#xff0c;围绕如何用轻量级 C 库构建高效本地程序展开&#xff0c;内容涵盖环境搭建、窗口与控件操作、消息映射、事件处理、UI 设计、ATL COM 组件开发、国际化、性能优化与调试测试等主题。资…

作者头像 李华
网站建设 2026/9/7 2:14:49

希捷酷鱼8TB机械硬盘实测:从验盘到迁移到长期使用的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 2:14:09

把大模型搬进内网:WeKnora + Ollama 本地化部署实操指南

把大模型搬进内网&#xff1a;WeKnora Ollama 本地化部署实操指南 【免费下载链接】WeKnora Open-source LLM knowledge platform: turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki. 项目地址: https://gitcode.com/…

作者头像 李华
网站建设 2026/9/7 2:11:30

让万物开口说话:从零搭建拍照识别智能体

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华