news 2026/9/13 13:04:51

PWM波生成全解析:从占空比计算到死区、DMA与故障保护实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PWM波生成全解析:从占空比计算到死区、DMA与故障保护实战

我用一个小场景开头吧。去年我调一块无刷驱动板,三路互补PWM死活出不来波形,示波器一挂,六路信号里有两路是齐的,另外四路互相乱抖。查了一整天,最后发现是死区寄存器没配,上下桥臂直通,电流直接把你板子上的一路采样电阻烧冒烟。从那天起我就意识到,PWM波这件事,会写两行代码配置定时器只是入门,真正决定波形能不能用、电路会不会炸的,是那些藏在高级定时器里没人给你画重点的寄存器位和硬件特性。

这篇学习笔记就是把PWM波的生成从底层到实战完整梳理一遍,涵盖占空比计算公式、不同芯片平台(51、STM32、PY32、Mega2560、RK3588)的生成方式、死区与中心对齐模式、PWM DMA搬运、故障保护,以及AO3400A这类MOS管驱动电路里容易被忽略的细节。适合刚接触PWM的新手建立体系,也适合已经写过PWM但一直“知其然而不知其所以然”的开发者查漏补缺。

1. 先从波形本身说起:占空比、频率与等效电压

1.1 占空比决定的是负载的“平均感受”

PWM波从外观上看就是一个方波:高电平、低电平、再高电平,循环往复。但真正驱动负载的时候,负载并不关心那一瞬间是多少伏,它感受的是整个周期内的平均效果。比如一个5V的方波,高电平占四分之一时间、低电平占四分之三时间,等效平均电压就是1.25V。LED会按这个平均电压呈现亮度,电机会按这个平均电压决定转速,加热棒按它决定功率。

这个“平均”对不同类型的负载有不同的物理含义。对LED这类阻性负载来说,电流和电压线性相关,人眼又有视觉暂留效应,所以1kHz左右的PWM就能实现无级调光。对电机这类感性负载来说,电感会产生续流效应,电流不会立即归零,而是被电感平滑成带有一定纹波的直流电流,因此PWM调压的效果比想象中更接近模拟电压。理解了这一点,很多后续问题——比如为什么同一个PWM信号驱动LED不闪但驱动舵机乱抖——就都解释得通了。

核心计算公式写在这里:

  • 占空比:D = T_on / (T_on + T_off) × 100%
  • 等效电压:V_avg = V_high × D
  • 频率:f = 1 / (T_on + T_off)

1.2 频率选择不是越大越好

频率代表的是“一秒开关多少次”。选低了,LED会闪、电机会一顿一顿;选高了,开关损耗上升、驱动电路可能跟不上。具体怎么选,取决于负载属性和系统设计目标。这里给出几个我实际用过的参数区间:

负载类型推荐频率选择理由
LED调光1kHz ~ 5kHz人眼在300Hz以上基本无闪烁感,过高意义不大
标准舵机50Hz接收机/舵机协议固定,周期20ms,脉宽0.5ms~2.5ms
普通直流电机10kHz ~ 30kHz避开人耳可听范围,同时纹波可被电感有效滤除
TEC帕尔贴100Hz ~ 1kHz过高会导致制冷效率降低,过低会出现温度波动和可闻噪声
BLDC/开关电源20kHz ~ 100kHz+需要配合电机电感/变压器参数做开关纹波设计

这里有一个反直觉的点:PWM频率不是越高越好。频率提高确实能减小电流纹波,但开关损耗和驱动电路损耗会同步上升。尤其是在用MOS管做功率开关时,一个开关周期内的开通损耗和关断损耗都是实实在在的发热来源,频率翻倍发热几乎翻倍。所以工程上选频率的原则是“够用就好”,先看负载对纹波和噪声的要求,再反推开关频率。

1.3 定时器里的占空比是怎么算出来的

写代码之前,得先理解单片机的定时器是如何“翻译”占空比的。以最常见的通用定时器为例,里面有三个关键寄存器:

  • PSC(预分频器):对定时器时钟进行分频
  • ARR(自动重装载值):计数器从0数到ARR,数满就是一个PWM周期
  • CCR(捕获/比较寄存器):计数器计到CCR时,输出电平翻转

所以:

  • PWM频率:f = 定时器时钟 / ((PSC+1) × (ARR+1))
  • 高电平时间:T_on = CCR / 定时器时钟 × (PSC+1)
  • 占空比:D = CCR / (ARR+1) × 100%

我举个实际算过的例子。某STM32芯片的定时器时钟是84MHz,我想生成一个20kHz的PWM。选PSC=41、ARR=99,频率就是84,000,000 / (42×100) = 20kHz。占空比50%时,CCR=50。这里要注意的是,ARR和PSC是两回事:PSC决定的是“计数一次要多长时间”,ARR决定的是“一共数多少次算一个周期”。两者共同决定频率,但CCR只与ARR共同决定占空比。很多人刚开始会误以为调节PSC也能改变占空比,其实PSC只影响频率。

2. 生成PWM的三条路线:软件翻IO、定时器中断和硬件输出

2.1 纯软件延时翻转:只适合理解概念

最直觉的写法就是拉高IO,延时,拉低IO,延时,再拉高,循环。核心代码大概长这样:

while (1) { GPIO_SetPin(HIGH); delay_us(50); // 高电平持续时间 GPIO_SetPin(LOW); delay_us(150); // 低电平持续时间 }

这种方式的优点只有一条:不依赖任何外设,只要会GPIO就能写。我第一次做呼吸灯用的就是它,LED确实能亮能灭,但做出来的效果很粗糙。问题出在三个方面:

  1. CPU被完全占死。延时期间什么都做不了,没法同时处理按键、显示、通信。
  2. 占空比不准。只要来了一个中断,ISR执行期间电平状态就被拉长了,波形直接失真。
  3. 频率做不高。延时函数的精度有限,想生成几十kHz的PWM基本不可能。

所以我的建议是:纯软件翻转只适合学概念的时候跑一下,确认“哦,原来PWM就是这个意思”,然后就没有然后了,正经项目千万别这么干。

2.2 定时器中断翻转:解决了占用,但精度依然有限

把延时改成“在定时器中断里翻转IO”,可以解决CPU被占死的问题。做法是让定时器按固定周期触发中断,每次中断里翻一次IO,再用一个变量记录当前处于高电平还是低电平状态。

void TIM_IRQHandler(void) { count++; if (count <= CCR) { GPIO_SetPin(HIGH); } else { GPIO_SetPin(LOW); } if (count >= ARR) { count = 0; } }

这种方式比纯延时好,CPU可以在主循环里干别的。但依然有隐患:中断响应本身有延迟,从“比较条件成立”到“真正执行GPIO翻转”之间隔着中断入栈、变量读取、IO操作的一条龙时间。频率越高,这个延迟占周期的比例越大,波形就越不规范。所以我用它做过呼吸灯、简单的舵机控制,但一旦涉及电机驱动或者对波形时序有要求的场合,还是得回到定时器硬件的轨道上。

2.3 硬件PWM输出:工程上的正解

大多数MCU的定时器都有“输出比较”或“PWM模式”。它的原理简单说就是:计数器的计数值是硬件自动累加的,无需软件参与;当计数值等于CCR时,硬件自动翻转输出引脚;当计数满ARR溢出时,硬件自动复位。整个过程中CPU只需要在初始化时写一次寄存器,之后就可以去睡觉了。

硬件PWM和软件PWM最大的区别在于确定性。电平翻转的时机由数字比较器硬件完成,不依赖指令执行时间、不依赖中断响应时间,所以波形的频率和占空比是严格确定的。打开“PWM模式”和“自动重装载预装载”之后,运行中修改CCR还能让占空比在下个周期平滑生效,这也就是呼吸灯、云台舵机、电机调速这类动态调占空比场景的标准做法。

后面的内容基本都围绕硬件PWM展开,因为这才是工程上真正能用、用了不慌的生成方式。

3. 不同平台的PWM生成实操:从51到RK3588

3.1 51单片机:定时器模拟PWM,以及驱动WS2811的硬骨头

51单片机的大部分型号不带硬件PWM,或者只带一个极简功能的PCA模块。所以51上做PWM的标准姿势是:用定时器产生一个固定时间片中断(比如10us),在里面维护一个计数器,把计数器和目标比较值做比较,决定IO高低电平。

void timer0_isr() interrupt 1 { counter++; if (counter < compare_value) { P1_0 = 1; } else { P1_0 = 0; } if (counter >= period_value) { counter = 0; } }

这套逻辑能跑出占空比可调的波形,做LED呼吸灯绰绰有余。但如果你拿去驱动WS2811/WS2812灯带,就会撞到南墙。WS2811的数据协议是纳秒级的:0码要求高电平约350ns、低电平约800ns,1码要求高电平约700ns、低电平约600ns。51的定时器中断进一次至少要几微秒,根本来不及逐bit输出。所以网上那些“51单片机驱动WS2811”的方案,要么用汇编做精细延时,要么用硬件SPI模块按位搬运,比如把8bit的亮度数据映射到16bit SPI数据,用2MHz左右的SPI时钟模拟时序。这种做法能跑,但相当折腾,而且主频不够的51很容易翻车。

3.2 STM32/PY32:通用定时器做PWM的寄存器级写法

到了STM32这个级别的MCU,PWM就变成了正经的外设功能。以STM32F103的TIM2为例,用标准库或HAL之外,我更推荐先看一眼寄存器级写法,能帮助理解硬件到底在干什么:

// 1. 开启定时器时钟,这里以APB1上的TIM2为例 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // 2. 配置PA0为复用推挽输出,对应TIM2_CH1 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; GPIOA->CRL &= ~(0xF << (0 * 4)); GPIOA->CRL |= (0xB << (0 * 4)); // 复用推挽输出50MHz // 3. 配置PSC和ARR,假设时钟72MHz,目标频率20kHz TIM2->PSC = 71; // 72MHz / (71+1) = 1MHz TIM2->ARR = 49; // 1MHz / (49+1) = 20kHz // 4. 配置通道1为PWM1模式,设置占空比 TIM2->CCMR1 |= (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1); // 110: PWM模式1 TIM2->CCMR1 |= TIM_CCMR1_OC1PE; // 输出比较预装载使能 TIM2->CCR1 = 25; // 占空比 = 25/50 = 50% // 5. 使能输出和定时器 TIM2->CCER |= TIM_CCER_CC1E; TIM2->CR1 |= TIM_CR1_CEN;

这段代码跑通了,PWM就在PA0上出来了。PY32F003这类国产小MCU思路完全一致,只是库函数不同。我之前用PY32F003做过一个呼吸灯demo:PWM输出接LED,主循环里每隔几毫秒把CCR从0递增到ARR再递减回来,LED的亮度就流畅地“呼——吸——”。整个过程几乎不占CPU时间,效果比软件PWM强太多。

3.3 Mega2560和RK3588:从Arduino到嵌入式Linux的PWM

Mega2560用的是AVR的Timer,如果不想折腾寄存器,Arduino的analogWrite()可以直接输出固定频率的PWM。但这里有个坑:analogWrite()的频率是固定的,大约490Hz(取决于Timer分频),想要调节频率得直接操作寄存器。比如说要配置Timer1输出10kHz的PWM,就必须自己改WGM、COM和OCR。Arduino封装了易用性,也把灵活性藏起来了。

RK3588这类带操作系统的SoC,PWM生成的思路又不一样。Linux下PWM通常由片上PWM控制器驱动,通过设备树配置好节点后,操作/sys/class/pwm/下的文件即可。我调RK3588风扇PWM时,流程是这样的:

# 在设备树里使能pwm-fan节点,配置好pwm通道和频率范围 # 系统启动后,导出PWM通道 echo 0 > /sys/class/pwm/pwmchip0/export # 设置周期(单位ns),比如40kHz echo 25000 > /sys/class/pwm/pwmchip0/pwm0/period # 设置占空比,50%就是12500ns echo 12500 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle # 使能输出 echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

从51到STM32再到Linux,平台变了,但底层逻辑完全一致:频率由周期寄存器决定、占空比由比较寄存器决定、最终输出由硬件自动翻转。把这套抽象模型吃透,换什么平台都只是换个寄存器名字的问题。

3.4 常见负载的PWM参数参考

我把几个典型场景的参数整理成一张表,方便直接抄作业:

应用对象频率占空比范围说明
LED呼吸灯1kHz0% ~ 100%动态改CCR即可
标准舵机50Hz2.5%~12.5%(脉宽0.5~2.5ms)1.5ms脉宽对应中位
直流电机调速20kHz0%~100%超过20kHz人耳听不到啸叫
TEC温控100Hz~1kHz0%~100%需要兼顾制冷效率
三相BLDC20kHz~50kHz由FOC算法计算需要6路互补PWM+死区

4. 高级定时器才有的功能:死区、中心对齐、DMA和故障保护

4.1 死区:为什么H桥/BLDC控制绕不开它

H桥或三相逆变桥驱动电机时,上下桥臂的两个开关管(MOS管或IGBT)是串联在电源两端的。如果上下两个管子同时导通,电源直接短路,电流瞬间飙升,板子必炸。理想情况下PWM切换瞬间是“上面关断、下面开通”的完美接续,但实际器件不是瞬间完成开关的——关断延迟比开通延迟长。

解决办法是在上下桥切换之间插入一段“都不导通”的时间,这就是死区。死区时间里上下桥臂全部关断,电流靠MOS管体二极管或外部反并联二极管续流。STM32的高级定时器TIM1/TIM8、英飞凌TC3xx的CCU6这类外设,都支持硬件死区插入:只要配置了互补PWM和死区时间,硬件会在每一条互补边沿自动插入延迟,不需要软件干预。

死区时间不是越大越好。死区太长,波形失真、电机电流谐波增加、输出效率下降;太短,关断延迟覆盖不掉,直通风险仍在。常见做法是:先查开关管的关断延迟和栅极驱动电路的传播延迟,再留出30%~50%的余量。我之前用TC3xx CCU6输出三相六路PWM控制BLDC,死区设置在1us左右,使用示波器观察上下桥的导通波形,确认没有交叠区间后才敢真正上强电。

4.2 中心对齐模式与ADC采样时刻的选择

常规PWM叫边沿对齐模式:计数器从0数到ARR,然后立刻回0,输出波形的高电平集中在周期一侧。中心对齐模式不一样,计数器从0数到ARR再从ARR数回0,输出波形的高电平被对称地放在周期中间。

中心对齐的优点是输出波形对称性好,谐波含量低,电机相电流波形更平滑,所以FOC矢量控制里普遍用中心对齐模式。但代价是:同样的计数器时钟和ARR,中心对齐的PWM频率降为边沿对齐的一半;或者说要保持同频率,需要的计数频率翻倍。

这个模式下有一个非常实际的坑:ADC采样的时刻。电机控制电流采样要避开功率管开关瞬间产生的噪声尖峰,最佳采样时刻是计数器的顶端或底端——也就是PWM周期的正中心,此时上下桥臂状态刚稳定,噪声最小。我当时第一次调STM32高级定时器中心对齐模式加ADC同步采样,总采到毛刺很大的电流波形,后来把触发源配置为定时器更新事件再偏移一点采样点,波形才干净。这件事说明:PWM的波形质量不仅在于生成端,还在于和ADC采样的时序配合。

4.3 PWM+DMA:用内存搬运替代CPU干预

光看“PWM输出”本身,CPU只需要配置一次。但很多场景需要动态改变占空比,比如WS2812整条灯带串行数据、多路舵机的连续角度轨迹、音乐频谱灯。一波数据几百上千个点,如果每个点都在中断里改一次CCR,CPU忙死且时序不稳。

PWM+DMA的思路很简单:把占空比序列预放在一个数组里,利用定时器的更新事件作为DMA请求源,DMA自动把数组中的值逐个搬运到CCR寄存器。CPU只管准备数组和启动DMA,中间过程不参与。以STM32 HAL库为例:

// 假设已经初始化好PWM输出 uint32_t duty_buffer[100]; // 占空比序列,提前计算好 HAL_TIM_PWM_Start_DMA(&htim, TIM_CHANNEL_1, duty_buffer, 100);

数据搬完可以触发DMA半传输/传输完成中断,在中断里填充下一段数据。这样就实现了“数据生成交给CPU、时序执行交给硬件”的流水线。我做WS2812灯带驱动的时候就是用的这个方案,一路PWM模拟数据位的脉宽,DMA把一个灯珠的GRB数据按bit展开成序列,几百个灯珠的刷新全程不卡顿。这也是网上“PWM DMA HAL”那类搜索词背后最典型的应用。

4.4 PWM故障保护(Break/刹车)机制

高级定时器通常都带故障保护引脚,比如STM32 TIM1/TIM8的BKIN引脚、TC3xx CCU6的故障输入。这个功能的设计初衷是:当外部电路检测到过流、过压、过温时,硬件在微秒或纳秒级内把PWM输出强制置于安全状态(比如全部输出低电平),不需要软件参与判断。

故障保护为什么重要?因为PWM控制电机或功率变换器时,软件响应速度根本赶不上故障的破坏速度。你可能在1us内就能烧掉一个MOS,但CPU从检测到中断到执行关断指令至少需要几百ns到几微秒,中间这段空白足以让功率器件冒烟。硬件故障保护直接锁死输出,是最后一道安全防线。

配置故障保护时要确认几个参数:故障输入极性(高有效还是低有效)、故障响应电平(输出高还是低)、故障后是否需要软件解锁。我在调试中遇到过一种情况:设置了BKIN后,PWM一有负载就立即保护,查了半天发现是外部比较器的参考电压设低了,正常工作时噪声触发了故障误判。这类问题排查起来确实头疼,但总比真炸管好。

5. PWM波生成之外的硬件细节:驱动级和电路设计的坑

5.1 单片机IO直驱还是驱动芯片?电流能力说了算

单片机的GPIO输出驱动能力一般在几毫安到二十毫安之间,带个LED没问题,带继电器、电机、灯带这类负载就力不从心了。PWM信号本身只是“控制信号”,真正驱动负载的是功率器件。

工程上最常见的接法:PWM从MCU引脚输出,经过一个限流电阻接到三极管或MOS管的栅极/基极,功率管再开关负载回路。这样负载的大电流走电源回路,不经过MCU引脚,MCU只负责提供几毫安的控制电流。如果负载电流更大或者开关频率更高,就用专门的栅极驱动芯片,比如半桥驱动、MOS驱动,它们内置电平转换、死区控制甚至故障反馈。

5.2 AO3400A这类N-MOS在PWM电路里的角色

AO3400A是一颗非常常见的低压N沟道MOS管,SOT-23封装,Vgs阈值电压大约在0.65V到1.4V之间,也就是说以3.3V或5V逻辑电平可以直接驱动,不需要额外电平转换。它的导通电阻Rds(on)在Vgs=4.5V时大概在几十毫欧级别,作为低压侧的PWM电子开关非常合适。

很多人直接把单片机PWM引脚接到MOS管栅极就算完事,但有几个细节值得注意。第一个是栅极电阻:栅极串联一个几欧到几十欧的电阻,用来抑制开关瞬间的振铃。电阻太小,栅极回路寄生电感会和栅极电容形成LC振荡,波形上出现明显过冲;电阻太大,开关边沿变缓,开关损耗增大。第二个是栅极下拉电阻:10k到100k左右的栅源下拉,防止MCU复位期间引脚悬空导致MOS误开通。这一点对“PWM上电瞬间乱跳”这类问题特别关键。

5.3 H桥PWM电路的数学原理

H桥是直流电机正反转控制的基础拓扑,四个开关管组成一个“H”形——负载接在中间横杠上,四个开关交替组合,让电流可以双向流过负载。PWM调速时,通常是固定方向,用PWM控制有效占空比。假设电源电压是Vbus,那么负载两端的平均电压就是:

  • V_motor = D × Vbus(前提是方向组合固定且上下桥未直通)

这也是直流电机PWM调速的基本公式。实际波形并不平滑,电机两端等效一个电阻加电感,电感对PWM电压起滤波作用,电流纹波大小和PWM频率、电感量、母线电压都有关系。频率越高、电感越大、母线电压越低,电流纹波越小。但频率高到一定值后,开关损耗成为主要矛盾,所以要综合权衡。

6. 我在实际调试PWM时踩过的几类坑

6.1 上电瞬间的意外PWM导致误动作

MCU上电复位期间,GPIO默认状态是浮空输入或弱上拉,PWM外设还没初始化。如果这个GPIO直接接到MOS管栅极,浮空状态下栅极电压可能超过阈值,负载在上电瞬间会“抖”一下或者误动作。我遇到过一上电风扇就猛转一下才停的问题,排查了很久,最后在栅极加了一颗100k下拉电阻,同时把初始化顺序改成“先配置GPIO为低电平输出,再初始化PWM复用”,问题彻底消除。

6.2 示波器探头带来的假波形

PWM频率上到几十kHz之后,示波器测量本身的寄生参数就开始影响测量结果。最典型的是那条带鳄鱼夹的接地线,线长十厘米左右就等效于一个分布电感,会和探头电容形成谐振,波形上出现看起来像信号本身在“振铃”的假像。我一度以为是驱动电阻没调好,折腾了几个小时,最后换成一个短接地弹簧,振铃立刻消失。用示波器看高频PWM时,接地线尽量短,测量点尽量靠近引脚。

6.3 电机啸叫和PWM频率的恩怨

直流电机在几百赫兹到几千赫兹的PWM下会发出刺耳的“吱吱”声,这是PWM电流纹波中的音频分量通过电机壳体辐射出来的。解决办法很直接:把开关频率提高到20kHz以上,超出人耳听觉范围。但提高频率后要留意两个问题:一是开关损耗变大,MOS管温度会升高;二是如果你的驱动芯片本身开关速度不够,高频下波形会变差。所以提高PWM频率前,先确认驱动方案能扛住这个开关速度。

6.4 舵机和WS2811这类“挑剔”负载

舵机的PWM虽然占空比只有几个百分点到十几个百分点,但脉宽的绝对值是关键。标准舵机要求周期20ms(50Hz),脉宽0.5ms到2.5ms对应0度到180度。如果你的定时器频率设计得不合适,CCR的步进精度不够,舵机转动时就会一跳一跳,不平滑。WS2811则是另一个极端,它的数据协议对高低电平的脉宽精度要求是纳秒级,用普通定时器中断模拟基本没戏,老老实实用硬件SPI或PWM+DMA方案。

最后说说我自己做笔记的习惯。每次调完一个PWM相关的问题,我都会把三样东西记下来:第一是波形截图,标出频率、占空比和异常点;第二是寄存器配置代码,注明关键参数怎么算出来的;第三是踩坑过程,记录现象、排查链路和最终原因。这些笔记积累起来之后,再遇到类似问题基本能凭记忆快速定位,不用每次都从头查数据手册。建议你也试试这个办法,比收藏一堆教程有用得多。这篇PWM波生成的笔记就先整理到这里,下一步我打算把PWM+DMA驱动WS2812灯带的完整工程和波形实测数据整理出来,到时候再继续聊。

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

CentOS源码编译安装Python 3.10完整指南与常见坑

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

作者头像 李华
网站建设 2026/9/13 13:04:18

STM32C542 ADC精度实战:参考电压、采样时间与PCB布局三大关键

1. 项目概述&#xff1a;为什么STM32C542的ADC电压采集不是“接上线就能用”的简单事&#xff1f; 你手头有一块STM32C542开发板&#xff0c;想测个电池电压、电源轨电压或者传感器输出——看起来就是把模拟信号接到PA0口&#xff0c;开个ADC&#xff0c;读个寄存器值&#xff…

作者头像 李华
网站建设 2026/9/13 13:02:37

如何把应用后端从 Convex 迁移到 SpacetimeDB

如何把应用后端从 Convex 迁移到 SpacetimeDB 【免费下载链接】SpacetimeDB Development at the speed of light 项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB 这篇文章面向正在把应用后端从 Convex 迁到 SpacetimeDB 的开发者。两个系统都包含数据库…

作者头像 李华
网站建设 2026/9/13 13:02:36

红黑树旋转操作详解:原理、类型与工程实践

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

作者头像 李华
网站建设 2026/9/13 13:02:01

VB.net+Access汽车配件网站源码解析:从跑通到迁移实战

简介&#xff1a;一套基于 VB.NET 语言和 Access 数据库开发的 ASP.NET 汽车配件公司网站完整源码&#xff0c;面向 Web 开发初学者、计算机相关专业学生以及需要搭建小型电商展示平台的技术人员&#xff0c;能帮助快速理解 ASP.NET Web Forms 从页面设计、事件处理到数据访问的…

作者头像 李华
网站建设 2026/9/13 13:01:04

嵌入式Linux软连接安全删除实践与避坑指南

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

作者头像 李华