news 2026/9/7 11:41:34

STM32F103驱动SPI屏幕移植LVGL:从底层显示驱动到应用层配置全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103驱动SPI屏幕移植LVGL:从底层显示驱动到应用层配置全解析

简介:面向STM32F103嵌入式开发者,一份自写可用的LVGL移植程序完整演示了通过SPI接口驱动显示屏并移植图形库的工程化流程,适用于课程设计、产品原型开发或GUI学习实践。压缩包共761个文件,整体仅3.77MB,以C/H源码为主体,辅以mk构建脚本、o目标文件和axf固件,便于直接编译烧录;md文档说明移植细节,png/bin提供界面素材与位图数据,结构清晰,适合对照学习与二次修改。目前已有4176人学习下载。内容覆盖SPI时钟分频、数据模式、极性相位等接口配置,常见屏幕驱动适配(如ST7789、SSD1306等)、帧缓冲管理,以及LVGL控件创建、事件驱动模型与动画效果;既包含屏幕初始化、画点刷新等底层函数,也提供按钮、滑块等对象的调用示例,并涉及系统时钟、电源管理、触摸屏接入与串口打印调试等工程实践,可直接作为快速搭建嵌入式GUI的基础框架,帮助开发者理解显示链路并降低移植过程中的排错难度。

1. 项目定位:为什么到现在还要折腾STM32F103加SPI屏跑LVGL

1.1 这套组合能干什么,以及它真正的边界在哪里

STM32F103这颗芯片放到今天真的不算年轻了,但你要说它过时,那市面上大量量产设备第一个不同意。72MHz的主频、20KB的RAM、丰富的外设资源,在简单的工控面板、智能家居小屏、DIY仪器表头上依然有大量应用场景。F103搭配一块SPI接口的TFT屏幕再移植LVGL,可以说是嵌入式GUI入门最经典的一套组合拳,也是很多新手从裸机点灯走向图形界面的必经之路。

网上关于LVGL移植的教程其实不少,但绝大多数要么只讲了LVGL本身的代码适配,对底层SPI屏幕驱动一笔带过;要么贴的代码零零散散,中断、延时、片选处理全是坑。我这次写程序的时候就想清楚了一件事:整个链路从SPI底层到LVGL应用层,每一环都得是自写、可控、可复现的。现在这套程序我已经在F103C8T6最小系统板上跑了两个多月,界面切换、控件刷新、中文字体显示都稳定,今天把这套移植过程完整拆给大家。

先说结论性的话:STM32F103跑LVGL,完全可行,但前提是你得接受它屏幕分辨率不能太大、交互动画不能太花哨。240x320的分辨率、RGB565颜色格式,简单页面切换和控件动画能跑到流畅级别,但你要是硬上高清大图、复杂3D效果,那确实不是这颗芯片该干的活。搞清楚边界,才不会移植到一半怀疑人生。

1.2 移植前必须理清的几个概念

写代码之前,有几个基础概念我建议先弄清楚,不然调试的时候会一头雾水。

SPI和IIC的区别,这是新手最容易问的问题。IIC是两线制半双工通信,一根时钟线一根数据线,速度一般跑400kHz就差不多了,优点是省引脚;SPI是四线制全双工通信,MISO、MOSI各走各的,时钟可以跑到几十MHz。屏幕刷新本质上是高速往显存里扔像素数据,数据吞吐量直接决定刷新帧率,所以TFT屏幕基本都选SPI而不是IIC。你在淘宝上看到的很多所谓IIC接口小屏,其实是通过某个转接芯片把IIC转成并口或者SPI内部协议,本质还是SPI或者并口在驱动。

硬件片选和软件片选的问题。STM32的SPI外设自带硬件NSS引脚,可以配置成自动拉低拉高来选择从设备,省掉CPU干预。听起来很方便,但实际用下来,在驱动屏幕这种对时序有要求的场景下,硬件片选的时序控制和多设备共享总线时的灵活性都不太够用,尤其是LVGL刷新过程中经常要连续发送几百字节数据,中间间隔越小越好,硬件NSS在这种场合反而容易出幺蛾子。我习惯的做法是找任意一个普通GPIO接屏幕的CS引脚,手动拉低拉高,完全可控。程序里多两句GPIO操作的时间成本,换来的是调试时的省心,这笔账非常划算。

2. SPI屏幕驱动移植实操

2.1 硬件连接和屏幕选型

先看硬件选型。主控我用的是最常见的STM32F103C8T6核心板,也就是俗称的蓝板/黑板小板,价格便宜、资料多、引脚也够用。屏幕选择的是2.4寸SPI接口TFT屏,驱动芯片是ILI9341,分辨率240x320,带SD卡槽和触摸的版本,但本次移植只用到液晶显示部分,触摸和SD卡后续单独聊。

接线方面,这块屏幕的常用接口定义如下,我就直接给出F103上实测可用的引脚映射:

屏幕引脚功能STM32引脚说明
VCC电源3.3V部分带背光的屏可以接5V
GNDGND共地必须接
CS片选PA4软件片选,GPIO控制
RST复位PA1低电平复位,需手动控制
DC/RS数据/命令选择PA2用GPIO切换指令和数据模式
SDI/MOSI主出从入PA7SPI1_MOSI
SCK时钟PA5SPI1_SCK
LED/BLK背光控制PA3高电平点亮背光
SDO/MISO主入从出PA6读屏幕寄存器才用,可选

选择SPI1是因为F103的SPI1挂在APB2总线上,主频可以跑到72MHz,而SPI2挂在APB1上,最高只有36MHz。虽然屏幕刷新用不了那么高频率,但SPI1的时钟源更干净,配出来的波特率误差更小,后续优化空间也更大。

2.2 SPI外设初始化:标准库直接可用的写法

我用的是STM32标准外设库V3.5版本,说实话HAL库和标准库各有拥趸,但F103上标准库的资料多、代码精简,做屏幕驱动这种偏底层的活反而更顺手。SPI初始化的代码直接贴出来,每个参数我都写了注释说明为什么这么配。

void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; /* 使能SPI1和GPIOA的时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA, ENABLE); /* 配置PA5(SCK)和PA7(MOSI)为复用推挽输出 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* 配置PA4(CS)、PA1(RST)、PA2(DC)、PA3(BLK)为通用推挽输出 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* SPI1主机模式,18MHz时钟 */ SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); }

SPI时钟分频这里专门说一下。STM32F103默认主频72MHz,SPI1挂在APB2上所以输入时钟是72MHz,Prescaler设为4,实际SPI时钟就是72/4=18MHz。对ILI9341来说,它的最大SPI时钟可以到40MHz甚至更高,所以18MHz是完全在规格内的。为什么不再往上超?因为18MHz已经能保证稳定刷新,频率越高对杜邦线长度、接触质量的要求就越苛刻,我的板子是飞线连接的,留点余量换取稳定更划算。

CPOL和CPHA的配置是屏幕能正常显示的关键,ILI9341默认支持模式0和模式3,即时钟空闲低电平、第一个边沿采样,或者时钟空闲高电平、第二个边沿采样。我这里选模式0(CPOL_Low + CPHA_1Edge),也是绝大多数SPI设备的默认配置。如果这里配错,最常见的症状就是屏幕花屏或者完全白屏,但代码看起来又没毛病,这个坑下面排查部分还会详细讲。

2.3 屏幕驱动芯片指令适配:写命令与写数据函数

SPI底层初始化完成后,接下来要实现对屏幕控制器的基本操作。ILI9341这类屏幕控制芯片,工作方式是通过DC引脚区分当前发送的是命令还是数据:DC为低电平时发送的是命令字节,DC为高电平时发送的是数据。这是所有SPI接口TFT屏驱动的基础逻辑。

/* 写命令:DC拉低 */ void LCD_WriteCmd(uint8_t cmd) { LCD_CS_LOW(); LCD_DC_LOW(); SPI1_SendByte(cmd); LCD_CS_HIGH(); } /* 写数据:DC拉高 */ void LCD_WriteData(uint8_t data) { LCD_CS_LOW(); LCD_DC_HIGH(); SPI1_SendByte(data); LCD_CS_HIGH(); }

SPI1_SendByte里面就是标准的等待发送寄存器空、然后往DR寄存器写数据、再等待接收完成:

void SPI1_SendByte(uint8_t byte) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1, byte); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); SPI_I2S_ReceiveData(SPI1); }

注意最后一个读操作,SPI是全双工协议,你发一个字节的同时硬件也会收到一个字节,如果不去读接收寄存器,RXNE标志会一直置位,导致后续数据发送异常。这个细节在只发不收的应用里特别容易忽略,但漏了就是各种诡异问题。

写完命令和数据的基础函数后,再配合ILI9341的初始化命令序列,屏幕就能点亮了。初始化序列网上有现成的,但我的建议还是对照ILI9341的datasheet逐条理解,比如0x28关显示、0xCF/0xED/0xE8这些寄存器组的电源配置、0x3A设置像素格式为RGB565等等。这样一旦屏幕显示异常,你就能大概定位是电源配置错了还是显示方向设置错了,而不是盲目换一组初始化代码碰运气。

3. LVGL图形库的接入与关键配置

3.1 LVGL版本选择与工程文件接入

屏幕底层驱动没问题后,就可以接LVGL了。现在LVGL主要分成v8和v9两个大版本,v9架构更现代、功能更丰富,但也移除了很多v8的旧接口,网上教程参差不齐。我的建议是如果你第一次移植、资料不多,老老实实选v8.3.x的最后一个稳定版,资料最多、踩坑案例最全,性能在F103上也经过充分验证。v9适合新项目、有人协助解决编译问题的情况。

把LVGL的源码文件夹整体复制到工程里,需要包含的核心部分就两个:src核心源码和lv_conf_template.h配置模板。把lv_conf_template.h重命名为lv_conf.h放到工程目录,然后在编译选项里定义LV_CONF_INCLUDE_SIMPLE,这样编译器才能找到这个配置文件。

有几点接入手工要特别留意。第一,不要在main.c里一股脑include所有LVGL头文件,只要包含lvgl.h就行,内部已经处理了所有依赖关系。第二,如果你的编译器是Keil MDK,记得把LVGL的.c文件分组整理好,漏文件会导致一堆莫名其妙的未定义错误。第三,LVGL源码里很多功能是用宏开关控制的,全部开启在F103上根本编译不过或者RAM爆掉,所以必须手动裁剪lv_conf.h,这个下面详细说。

3.2 lv_conf.h里必须调的几个关键参数

lv_conf.h是整个LVGL配置的心脏,配置对了项目就成功了一大半。对F103这套硬件,我的建议配置如下:

  • LV_COLOR_DEPTH 16:颜色深度选16位,正好对应该屏幕RGB565格式。这里有个核心逻辑:LVGL内部用RGB565处理像素,屏幕驱动直接把LVGL给的像素缓冲区原样搬到SPI总线上发给屏幕,全程不需要颜色转换,这可比ARGB8888省了一大截CPU和带宽。
  • LV_MEM_SIZE (16 * 1024):LVGL动态内存池大小。F103只有20KB RAM,这块我给了16KB,留给系统和其他模块的只有4KB,这个比例需要根据你的实际程序调整。如果你同时跑RTOS或者有其他大缓冲区,内存池可能得缩到8~10KB。
  • LV_TICK_CUSTOM 1:使用自定义时钟源。LVGL内部需要ms级时基,直接用一个现有的系统时基,避免LVGL独占SysTick。
  • LV_FONT_MONTSERRAT_14 1:默认字体。只开一个常用大小的西文字体就够调试用了,中文需要额外取模或者使用LVGL的字体转换工具生成。
  • LV_MEM_CUSTOM 0:用LVGL自带的动态内存管理,不对接标准库malloc。标准库的malloc在长时间运行后碎片化问题比较麻烦,LVGL自带的分块管理机制在小内存场景下更可控。

LV_MEM_SIZE这个参数的调整值得多说一句,它直接决定了你能创建多少控件、多大画布。开小了,运行时界面还没出来就直接断言报错;开大了,其他任务的内存被挤爆。我在调试的时候会在LVGL的lv_mem_monitor函数里打点观察实际内存占用,等界面稳定后再把内存池大小调到峰值占用加30%的余量,这样既不浪费RAM又不会运行时崩。

3.3 最关键的一步:显示驱动回调函数与刷新时钟

LVGL的应用层和底层屏幕之间靠一个显示驱动结构体衔接,lv_disp_drv_register注册时,会要求你提供一个flush_cb回调函数。LVGL内部逻辑在需要更新屏幕时,会把脏矩形区域和像素数据打包好,通过这个回调发给底层。

static void disp_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { /* 设置屏幕显示窗口为LVGL要刷新的区域 */ ILI9341_SetWindow(area->x1, area->y1, area->x2, area->y2); /* 利用DC数据模式,连续发送像素数据 */ LCD_CS_LOW(); LCD_DC_HIGH(); uint32_t pixel_count = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1); uint8_t *data = (uint8_t *)color_p; for (uint32_t i = 0; i < pixel_count * 2; i++) { SPI1_SendByte(data[i]); } LCD_CS_HIGH(); /* 通知LVGL这一帧数据已经发送完成,可以继续下一块 */ lv_disp_flush_ready(disp_drv); }

ILI9341_SetWindow函数就是发送0x2A列地址设置和0x2B行地址设置命令,把屏幕上的坐标窗口指定到LVGL传进来的脏矩形区域。这里有个大多数人容易忽略的点:显示屏的坐标原点和SPI方向可能跟LVGL期望的不一致,比如屏幕是竖屏还是横屏,要把底层SetWindow里的x和y做一次映射,否则会出现画面旋转90度或者显示错位的问题。

flush_cb里面有几个性能要点。第一,回调函数里不要做任何延时操作,能省则省;第二,如果编译器开启了优化,大数据块的连续发送可以考虑DMA来加速,这点下面单独说;第三,lv_disp_flush_ready一定要在数据全部发送完后调用,漏掉会导致LVGL认为刷新还没结束,画面卡住不动。

然后是LVGL的心跳和任务调度,main函数里加入这两段代码:

/* SysTick中断里调用,或者每隔1ms调用一次 */ void SysTick_Handler(void) { lv_tick_inc(1); } /* 主循环里循环调用,频率不要低于10ms一次 */ while (1) { lv_timer_handler(); delay_ms(5); }

lv_tick_inc提供LVGL内部的时间基准,lv_timer_handler负责处理所有的LVGL内部任务,包括动画推进、控件状态更新、输入设备轮询。如果你跑的是裸机程序,主循环里lv_timer_handler调用越频繁,界面响应越跟手,但CPU占用也越高。5ms一次的节奏是我实测下来比较均衡的。

3.4 刷新性能分析与优化方向

很多人关心SPI屏的刷新率到底能到多少,这里可以做一道简单的算术题。SPI时钟18MHz,传输8位数据,理论吞吐量18Mbps也就是2.25MB/s。一帧全屏240x320像素,RGB565格式每个像素2字节,全屏数据量就是240x320x2=153.6KB。理论上极限全屏刷新约14帧/秒,但别忘了实际传输还要加上命令字节、数据间隔、CS切换等开销,真实数据会再打折扣。

好在LVGL的刷新机制是局部刷新,它只更新变化的区域而不会每次都重画整屏。举个例子,你点击一个按钮弹出提示框,实际刷新的也就是那个提示框附近的一小块区域,可能只有几十K的数据量,这样感知上的流畅度就会好很多。实际用下来,简单的页面切换和动画在F103上可以做到响应迅速,视觉流畅度完全能接受。

如果你想继续压榨性能,最有效的优化是给SPI传输加上DMA。把flush_cb里的for循环发送改成DMA一次性搬移,CPU在DMA搬运数据的同时可以去处理LVGL的其他任务,总吞吐量能提升不少。具体做法是初始化一个SPI TX DMA通道,在flush_cb里设置好DMA目标地址和长度,启动传输,然后在DMA传输完成中断里调用lv_disp_flush_ready。这个优化我后面可以单独开一篇来讲,今天先记住结论。

4. 移植中的典型问题与排查实录

4.1 白屏的三种常见原因和定位方法

白屏是SPI屏幕移植时出现频率最高的问题,也是最容易让人慌的问题。按照我踩过的坑,白屏基本就三个原因:背光没点亮、复位时序不对、初始化命令没执行。

背光没点亮最常见的表现是屏幕完全漆黑,你甚至看不清是不是白屏。排查方法很简单,万用表量一下屏幕LED引脚电压是不是高电平,或者干脆先用飞线把一个3.3V接到LED引脚,屏幕亮了说明背光通路有问题,和代码没半点关系。

复位时序不对的表现更隐蔽:屏幕亮着但全白。ILI9341上电后必须给一个低电平脉冲完成复位,而且复位后需要等待足够长的稳定时间。我的初始序列里是CS高、RST拉低、延时20ms、RST拉高、再延时120ms。延时太短,芯片内部状态机还没准备好,初始化命令发出去它根本不理你,屏幕自然就全白。

初始化命令没执行或执行出错,这个就挨个查你的ILI9341_Init函数有没有被调用、SPI有没有先初始化、WriteCmd函数里时序对不对。我遇到过一次特别折腾的:SPI初始化代码写在LCD初始化之后,结果屏幕一直白屏,查了半天才发现是顺序问题,MCU连SPI都没就绪就去操作屏幕了,所有数据都白白丢在了总线上。

4.2 花屏和残影的根因

花屏这个症状比白屏好排查,因为它基本都和SPI通信质量有关。最常见的原因就是SPI的CPOL/CPHA配置和屏幕不匹配,数据错位导致颜色完全是乱的或者出现规律的色彩条纹。我调试时拿逻辑分析仪看过SPI总线波形,发现屏幕在模式0下工作正常,换到模式3就花屏,所以这里强烈建议先检查SPI的模式配置。

第二个常见原因是接线问题。杜邦线太长、接触不良、飞线和电源线绕在一起,都可能导致高速信号失真。18MHz的频率虽然不算高,但如果你用的是面包板加长跳线的连接方式,信号完整性问题就会开始显现。解决办法是缩短线距、用双绞线把SCK和GND缠在一起,或者把SPI时钟降到9MHz试试。我实际把主控板屏幕之间的线控制在10cm以内后,花屏问题几乎绝迹。

残影问题一般是窗口设置不对导致的。LVGL只刷新脏矩形区域,如果底层SetWindow设置的窗口和像素数据的实际排列不对应,就会出现部分区域老画面没被盖掉、看起来像残影的情况。解决方法是仔细核对ILI9341_SetWindow四个坐标参数的计算逻辑,特别注意列地址和行地址的低字节、高字节都要正确写入,顺序不能颠倒。

4.3 内存不足导致LVGL崩溃的处理

F103的RAM是20KB,LVGL内存池给了16KB,听起来不少,但如果你不知道怎么省着用,16KB很快会爆掉。LVGL内存不足时最常见的现象是界面初始化到一半突然卡死,或者进入HardFault中断。

排查方法很直接:在接线后跑一个简单的多个页面创建测试,然后在创建每个新对象后调用lv_mem_monitor打印空闲内存。你会发现创建多个样式、字体、图片对象时内存消耗非常快,尤其是Image控件是内存大户。解决手段有几个方向:降低LV_MEM_SIZE到10KB但优化对象创建逻辑;对象尽量用static声明而不是手动new;图片用外部flash存储,不要一次性全加载进RAM;最激进的做法是把控件动态创建改为预创建,用lv_obj_set_hidden控制显隐,避免运行期频繁的malloc和free。

这里有个实用的经验:如果程序经常在创建某个特定控件时崩溃,先查这个控件是不是用了不支持的样式属性,再查是不是内存不足,最后查是不是调用了没有开启的组件。LVGL里很多组件默认是关闭的,比如图表lv_chart、仪表lv_meter,你没在lv_conf.h里开启宏开关就调用相关API,编译能通过但运行必崩,这个坑我踩过一次之后,每次新版本配置都会系统性地过一遍组件开关列表。

4.4 常见问题速查表

最后把我的踩坑经验归纳成一张表,方便大家对照排查。

现象可能原因排查办法与解决办法
屏幕完全黑背光未点亮检查LED引脚电平,直接飞线3.3V验证背光通路
屏幕全白复位时序不对确认RST低电平脉冲宽度和拉高后的延时,加大到50ms/200ms
屏幕全白SPI未初始化确认SPI初始化在LCD初始化之前执行
花屏、彩色条纹CPOL/CPHA配置错误数据手册确认屏幕支持的模式,调整为模式0或3
花屏接线过长、接触不良缩短线距,降低SPI时钟到9MHz验证
显示错位/残影SetWindow坐标计算错误核对横竖屏映射,逐字节检查列/行地址高低位顺序
画面卡住不动漏掉lv_disp_flush_readyflush_cb里所有数据发送完成后必须调用
控件创建时崩溃LV_MEM_SIZE过小用lv_mem_monitor监控内存,扩大内存池或简化界面
编译失败提示未定义lv_conf.h组件开关未开启检查对应组件的宏定义,比如LV_USE_CHART、LV_USE_METER
控件创建时崩溃组件功能未使能逐个开启LV_USE_XXX宏,重新编译

5. 最后再分享一点实际操作中的经验

这套移植程序跑下来,我最大的体会是:底层的SPI屏幕驱动才是整个链路里最容易出问题的地方,LVGL反而只是配置文件里几个参数的事。很多人一上来就急着看LVGL的源码,到处找移植模板,结果怎么都调不出来,最后发现是SPI初始化或者屏幕初始化数组有问题。所以我强烈建议大家分两步走,先把屏幕裸机驱动调好,自己写一个全屏填充红色、画个矩形之类的测试代码,确认整条SPI通路的底层没问题了,再往上接LVGL,这样出问题的时候定位范围会小很多。

另外,如果你是第一次接触LVGL,千万别急着往F103上搬你精心设计的复杂界面。先在电脑上把LVGL模拟器跑起来,用鼠标点点看看,把布局、交互逻辑摸熟了再移植到单片机上改。模拟器和单片机端的差异主要集中在资源限制,逻辑层代码基本可以无缝移植。先在模拟器里验证你的想法,再在板子上调试性能,这个流程能帮你省下大量重复编译下载的时间。

我是用标准库写的这套程序,但这套思路同样适用于HAL库,无非是底层SPI发送函数换几个HAL的API,上层的LVGL集成逻辑完全一致。后续我再考虑把触摸屏驱动、中文字体显示、SD卡图片加载这几块内容也补上,让F103这个小板子真正变成一个能落地的小型GUI设备。

本文还有配套的精品资源,点击获取

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

跑山智驾零接管背后:智驾系统如何应对复杂山路挑战

/* 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 11:39:39

飞书多维表格深度解析:从在线表格到轻量数据管理平台

/* 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 11:38:53

AI Slop治理实战:从提示词到流程,彻底告别低质AI内容

/* 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 11:38:40

从补全到智能体:Codex五年进化路线与2025实战指南

/* 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 11:38:35

技术博客写作全攻略:从项目部署到问题排查

/* 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 11:38:25

Agent开发工程师指南:从Function Calling到企业级工程实践

Agent开发工程师&#xff1a;企业级能力塑造指南 最近频繁被问到同一个问题&#xff1a;Agent开发是不是就是调大模型API&#xff1f;如果只是把ChatCompletion换成带工具调用的接口&#xff0c;那这个岗位和普通后端开发有什么区别&#xff1f; 这个问题背后有一个更关键的困…

作者头像 李华