news 2026/9/28 1:19:34

STM32 UART驱动TM1652数码管:从CubeMX配置到动态显示实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32 UART驱动TM1652数码管:从CubeMX配置到动态显示实战

先交代一下背景。前段时间做一台四位数码管显示的温控器面板,一开始按老思路用单片机的IO口直接扫描数码管——段选8个IO、位选4个IO,加起来12个引脚,动态扫描还得定时器分时切片,刷ADC、扫按键、刷新显示全挤在一起,主循环忙得不可开交,代码写起来也别扭。后来换了个方向,直接用串口驱动TM1652这类数码管驱动芯片,两根线就把活干完了,亮度还匀,代码也干净。这套方案跑通之后我挺想整理成文的,因为网上关于TM1652的资料大多是裸机寄存器版,或者零散的点灯demo,很少有人把STM32CubeMX配置、HAL库驱动封装、动态显示刷新策略这三个环节串起来讲完整。

这篇文章就按照我实际做项目的顺序来写:先讲TM1652的通信原理和指令格式,再讲CubeMX里怎么配UART,然后封装HAL层驱动,接着做动态显示,最后是实测中踩过的三个坑和排查过程。文中用到的代码是基于STM32F103和HAL库的,F0/F4/G0系列只需要改一下时钟配置和引脚映射,整体思路完全通用。

1. TM1652到底怎么用串口去驱动数码管:协议和内部结构

1.1 一块芯片替掉12根IO信号线

数码管显示的驱动方案大概分三类:IO口直接扫描、串转并扩展芯片(比如74HC595)、智能LED驱动芯片。IO口扫描是最原始的做法,优点是成本低,缺点是引脚占用太高,而且扫描时序必须由主控全程维护——一旦主循环被其他任务挤占,数码管就会出现肉眼可见的闪烁。74HC595用3根线换8个输出引脚,属于折中方案,但段码表、位选逻辑、扫描时序还是得软件自己算。TM1652这类智能驱动芯片更进一步,它把显示RAM、译码电路、段驱动和位驱动全做在芯片内部,主控只要通过串口告诉它“每个位显示什么内容”,剩下的刷新工作芯片自己完成。

对做工程的人来说,TM1652最大的价值就是让显示任务和主控逻辑彻底解耦。主控要显示一个数字,发一帧数据就结束了,不需要在定时器中断里做动态扫描,CPU占用几乎是零。这个特性在实时性要求较高的场景里非常有用,比如温控器要同时处理PID计算、按键扫描、通信协议解析,如果还要挤时间刷数码管,系统复杂度会高一大截。

TM1652内部实际上就是一个寄存器组外加段驱动电路。每个显示位对应一个显示缓存寄存器,主控通过串口往这些寄存器里写数据,芯片自动把数据字节转换成段选和位选信号,驱动外接的共阴或共阳数码管。它还支持亮度调节,通过内部PWM占空比控制段电流的平均导通时间,实现多级灰度显示。

1.2 帧格式解析:地址字节加数据字节

TM1652走的是异步串口协议,常见配置是9600bps、8N1,也就是8个数据位、无校验、1个停止位,这跟标准UART完全一致。通信以帧为单位,帧结构里包含地址/指令字节和若干数据字节。指令字节的高位部分代表命令类型,低位部分代表操作地址;数据字节就是写入该地址显示缓存的值。

这里要特别强调一个容易混淆的点:TM1652显示缓存里的数据字节,和传统数码管段码表不是同一套体系。传统7段码是按“共阴/共阳+位选”手动计算的,比如数字1的共阴段码是0x06,对应b、c两段点亮。TM1652的数据字节直接描述的是各段的状态,1为亮、0为灭,发送之前要把“用户想显示的数字”转换成“段位图”,再进行封装。

段位图用一个静态数组维护最方便,查表发送即可。我调试时用的映射数组长这样:

// 按 TM1652 的段位映射:(dp g f e d c b a) // 注意:如果芯片批次不同,段序可能偏移,务必以实测为准 const uint8_t tm1652_font[] = { 0x3F, // 0: 段 a b c d e f 0x06, // 1: 段 b c 0x5B, // 2: 段 a b d e g 0x4F, // 3: 段 a b c d g 0x66, // 4: 段 b c f g 0x6D, // 5: 段 a c d f g 0x7D, // 6: 段 a c d e f g 0x07, // 7: 段 a b c 0x7F, // 8: 全部点亮 0x6F, // 9: 段 a b c d f g };

第一次接触这颗芯片的时候,我直接套标准共阴段码表,结果数字形状全乱,2显示成5,5显示成2。后来老老实实做了个全亮测试程序,把数据字节逐段点亮,对照数码管的物理段位置,才把映射关系彻底理清楚。这一步省不得。

1.3 亮度控制和软件消隐

除了显示缓存,TM1652还有亮度控制指令。寄存器里有一个亮度等级字段,通过配置PWM占空比来调节段电流的平均导通时间。调试阶段我习惯把亮度调低一点,因为高亮度下数码管发热明显,而且功耗差别不小,对电池供电的设备影响很大。量产时如果外壳是半透明材质,亮度等级也需要重新标定,否则会出现透光不均匀的问题。

TM1652还支持软件消隐,也就是在不改变显示缓存内容的情况下整体熄灭数码管,或者单独熄灭某一位。这个功能在实现闪烁效果时特别实用——不需要重发所有数字的段码,只要发一条开关控制指令,就能让整屏按指定节奏闪烁。后面做倒计时提示的时候会用到这个特性。

2. CubeMX配置UART的完整过程:从新工程到串口就绪

2.1 引脚规划与时钟树检查

我用的开发板是STM32F103C8T6,也就是最常见的“蓝丸”板,但整套流程在F0/F4/G0上都通用。TM1652对UART引脚没特殊要求,只要USART外设的TX引脚能正常输出电平就行,我用的是USART1的PA9(TX)和PA10(RX),其中真正用到的只有PA9,PA10只是顺便配置一下。

打开STM32CubeMX,选好芯片型号后,先在System Core里把RCC配置为HSE外部晶振,然后到Clock Configuration里把系统主频配到72MHz。这里要特别注意:UART波特率的精度取决于外设时钟源,如果板子上实际焊接的晶振频率和CubeMX里选的频率不一致,比如板子上是12MHz晶振而配置里按8MHz算,最终波特率就会明显偏移,TM1652这种简单的接收端可能直接解析失败。

配置完时钟后,在时钟树界面右侧可以看到“USART1 9600 bps”这个参数旁边显示的波特率误差百分比。误差不为0是正常的,只要低于0.5%基本都稳,超过1%就得考虑换一个波特率档位,或者调整主频参数。这个检查过程只需要几秒钟,却能省掉后面一大半的排查时间。

2.2 UART参数设置:波特率、数据位、停止位

在Connectivity -> USART1页面里,参数按照下面的配置填:

  • Mode: Asynchronous(异步模式)
  • Baud Rate: 9600
  • Word Length: 8 Bits
  • Parity: None
  • Stop Bits: 1

这三个参数组合起来就是8N1格式,也是串口设备最通用的默认配置。我特意选了9600而不是115200,原因很实际:数码管驱动芯片的成本摆在那里,高速率下的电平上升沿质量和抗干扰能力都堪忧。用杜邦线连接时,我试过跑115200,波形有明显振铃,显示偶尔错位;降到9600之后基本零错误。如果项目对刷新率有更高要求,或者需要级联多片TM1652,那可以考虑提升波特率,但前提是硬件连接要过关——双绞线或屏蔽线加共地是必须的。

还有一点容易被忽略:如果CubeMX里同时启用了USART1的RX引脚,但实际没有把RX接到任何设备上,这个悬空引脚可能引入干扰毛刺,导致UART外设状态机进入错误状态。我当时的做法是在CubeMX里把PA10引脚单独配置为GPIO输入下拉模式,而不是复用为USART1_RX,这样既不会影响TX发送,又避免了悬空引脚的噪声问题。

2.3 生成工程后的初始化代码检查点

CubeMX生成的代码里,HAL_UART_Init函数会根据配置参数来设置串口寄存器。生成工程之后我会例行检查三处:

第一处,检查GPIO复用配置是否正确,USART1_TX应该被配置为AF推挽输出模式,而不是普通的GPIO输出模式。CubeMX通常会自动处理,但升级HAL库版本时偶尔会出意外,瞄一眼总没坏处。

第二处,看波特率分频值是否合理。HAL库内部通过UART_DIV_SAMPLING16宏计算BRR寄存器值,这个计算依赖SystemCoreClock变量。如果实际主频是72MHz,但SystemCoreClock还是默认的8MHz,发送速率就会差一大截。在SystemClock_Config()函数里确认SystemCoreClock已经正确更新,这一步能避免一个很隐蔽的坑。

第三处,检查中断优先级。如果后面要用HAL_UART_Transmit_IT中断发送,需要在NVIC设置里把USART1全局中断使能打开,并且给一个合理的优先级。这个优先级建议不要和定时器中断设成同一个抢占优先级,避免出现不明确的中断嵌套行为。

2.4 串口链路的第一个测试程序

配置完并生成工程后,先不要急着写复杂功能,做一个最小化验证:发送一帧固定数据给TM1652,让数码管显示数字8。

int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); uint8_t test_data[2] = {0x01, 0x7F}; // 实际指令码以数据手册为准 HAL_Delay(50); // 等待TM1652上电稳定 HAL_UART_Transmit(&huart1, test_data, 2, 100); while (1) { } }

如果第一次上电就能点亮数字8,说明CubeMX配置、引脚映射、芯片接线全部没问题。如果没有反应,先别急着怀疑TM1652,用示波器或逻辑分析仪挂在PA9引脚上看波形。有波形说明MCU侧发送正常,问题出在指令码或者硬件接线;没波形就要回到CubeMX配置上排查。这个“先看波形再改代码”的顺序,能省掉大量盲改时间。

3. HAL层驱动封装:把串口字节流变成“数码管显示API”

3.1 发送函数选择:阻塞发送、中断发送还是DMA

HAL库提供三种UART发送方式:HAL_UART_Transmit是阻塞轮询,函数要等数据全部发送完才返回;HAL_UART_Transmit_IT是中断发送,配置好寄存器后立即返回,数据在中断里逐字节发送;HAL_UART_Transmit_DMA是DMA发送,数据由DMA控制器搬运,CPU完全不参与。

TM1652的显示指令很短,一般也就2到4个字节,9600波特率下发送一帧数据要2到4毫秒。阻塞发送看起来也没啥问题,但我建议至少用中断方式,原因不是“省CPU”这种虚词,而是避免阻塞卡死整个系统。如果主循环里还有其他周期性任务,比如按键扫描,阻塞发送期间如果UART出现断线或异常标志,HAL_UART_Transmit可能会一直卡在等待超时的循环里,导致整个系统假死。

中断发送就不存在这个问题,它只负责写寄存器和使能中断,立马返回主循环。即使UART出现错误状态,也只是触发错误回调函数,不会卡死主循环。

DMA方式适合一批数据连续发送的场景,比如级联多片TM1652时一次要发送十几字节。但DMA有一个坑:发送是异步的,如果发送过程中修改了源缓冲区的内容,DMA可能把新数据发出去而不是当前帧的数据。解决方法是使用独立的发送缓冲数组,在DMA传输期间绝对不要碰它。

3.2 驱动框架:初始化、清屏、写显示寄存器

我封装的TM1652驱动包含四个函数,核心操作只有一个——写寄存器。

static void TM1652_WriteRegister(uint8_t reg_addr, uint8_t reg_data); void TM1652_Init(void); // 上电初始化:清屏、设亮度 void TM1652_Clear(void); // 清空显示缓存 void TM1652_DisplayString(const char *str); // 显示4位以内字符串 void TM1652_SetBrightness(uint8_t level); // 设置亮度等级

TM1652_WriteRegister内部做组帧和发送。为了保证前一帧发送完成再发下一帧,用一个tx_busy标志串行化发送过程:

static volatile uint8_t tm1652_tx_busy = 0; void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { tm1652_tx_busy = 0; } } static void TM1652_WriteRegister(uint8_t reg_addr, uint8_t reg_data) { uint8_t buf[2] = {reg_addr, reg_data}; while (tm1652_tx_busy) { } tm1652_tx_busy = 1; HAL_UART_Transmit_IT(&huart1, buf, 2); }

这个代码里藏着一个特别隐蔽的bug:buf是局部变量,中断发送是异步的,中断服务函数会在后台读取这块内存。如果函数退出后栈空间被其他函数复用,数据就会错乱。我第一版驱动就是这么写的,调试助手抓到的数据偶尔会冒出几个错误字节,排查了很久才定位到。正确做法是使用static修饰缓冲区,或者直接把缓冲区定义成驱动文件内部的全局数组:

static uint8_t tm1652_buf[4];

配合tx_busy标志来串行化发送,既避免了缓冲区覆盖,又不需要动态内存分配。

3.3 显示字符串:查表映射成段码再组帧

显示一个四位的字符串“1234”,逻辑是逐字符查表得到段位图,然后按地址组装成TM1652的显示缓存数据。如果芯片支持地址自动递增模式,可以一次连续发送多个数据字节;否则就要一帧一帧写。我这里按最常见的“每帧带地址”的形式来写:

void TM1652_DisplayString(const char *str) { uint8_t seg_data[4] = {0, 0, 0, 0}; for (uint8_t i = 0; i < 4 && str[i] != '\0'; i++) { char c = str[i]; if (c >= '0' && c <= '9') { seg_data[i] = tm1652_font[c - '0']; } else if (c == '-') { seg_data[i] = 0x40; // 负号用 g 段 } else if (c == ' ') { seg_data[i] = 0x00; // 空格熄灭所有段 } } // 组帧:从地址0x01开始,依次写4个显示缓存 tm1652_buf[0] = 0x01; tm1652_buf[1] = seg_data[0]; tm1652_buf[2] = seg_data[1]; tm1652_buf[3] = seg_data[2]; while (tm1652_tx_busy) { } tm1652_buf[4] = seg_data[3]; tm1652_tx_busy = 1; HAL_UART_Transmit_IT(&huart1, tm1652_buf, 5); }

注意这里只做了示意,具体地址需要按芯片数据手册来。重点在于这套“查表、组帧、串行发送”的驱动骨架,换任何一颗LED驱动芯片都能复用。

4. 动态显示实战:倒计时秒表、小数点与滚动刷新

4.1 显示缓冲区设计:传统扫描和智能驱动芯片的根本差异

传统IO扫描的动态显示,主控必须在定时器中断里周期性切换位选,频繁刷段码数据,因为数码管本身没有数据保持能力,全靠刷新频率维持视觉暂留。TM1652这类芯片在内部实现了锁存,只要不发新数据,显示内容就一直保持。所以主控的任务从“不断刷新”变成了“事件触发刷新”——显示内容变化时才需要发一次数据。

这个差异对软件架构影响很大。应用层只需要维护一个display_buffer[4],业务逻辑更新数值时改buffer,然后调用一次TM1652_DisplayString即可。比如做倒计时秒表:

volatile uint16_t countdown_centisecond = 5999; // 59.99秒 char display_str[5]; void Timer_UpdateDisplay(void) { uint16_t cs = countdown_centisecond; display_str[0] = '0' + (cs / 1000) % 10; // 十秒位 display_str[1] = '0' + (cs / 100) % 10; // 秒个位 display_str[2] = '0' + (cs / 10) % 10; // 十分之一秒 display_str[3] = '0' + cs % 10; // 百分之一秒 TM1652_DisplayString(display_str); }

定时器中断里每10ms更新一次计数,数值变化时触发显示更新。因为TM1652自带锁存,动态刷新的压力比传统扫描小了一个数量级,CPU占用也低得多。

4.2 小数点的处理:段位图按位叠加

四位数码管显示“59.9”这种带小数点的内容时,小数点不能单独占一位。它实际上挂在某个数字段上,处理方式是在组帧时对某一位的段码额外叠加DP段的位(通常是最高位bit):

seg_data[1] |= 0x80; // 第2位加小数点,0x80是DP段位置

我建议把“加小数点”的逻辑封装成独立函数,不要在字符串里直接塞.字符,因为.在TM1652的显示RAM里不是一个独立的位,而是附着在段位图上的附加位。混在一起处理很容易出问题。实测下来,先把数字段码确认好,再按位或上DP位,是最稳妥的做法,代码可读性也好很多。

4.3 滚动显示长文本:窗口索引翻转

如果显示内容超过4位,比如要循环显示产品型号“STM32F103”或者一组操作提示,就需要滚动效果。滚动实现的核心是一个窗口索引:

const char full_text[] = "STM32F103"; int16_t window_start = 0; void Scroll_TimerTick(void) { window_start++; if (window_start > (int16_t)(sizeof(full_text) - 5)) { window_start = 0; } char window[5] = {0}; strncpy(window, &full_text[window_start], 4); TM1652_DisplayString(window); }

注意full_text要预留足够的长度,strncpy只复制4字节,窗口起始索引超界前要及时回绕。滚动屏幕的刷新率不用太高,200ms左右刷新一次人眼看着刚好,太快会显得急促,太慢又显得拖沓。这里的timer可以是一个软件定时器,在主循环里计数实现,没必要额外占一个硬件定时器。

4.4 动态显示与多位小数点的组合应用

把上面几个点组合起来,可以实现一个比较完整的效果:倒计时显示到最后一秒时,数码管上显示“0.0”,并且整体闪烁提醒。

核心逻辑是,倒计时主循环里更新数值;当剩余时间少于1秒时,在字符串里附加特殊标记,驱动层检测到标记后不改变数字,而是通过开关控制指令周期性熄灭和点亮数码管。这样做的好处是数字段码不需要反复重发,只需要发送一条开关指令,帧数据短,时序干扰小。

// 主循环 if (countdown_centisecond < 100) { // 最后1秒,每200ms翻转一次显示开关 static uint8_t blink_on = 1; if (HAL_GetTick() - last_blink_tick >= 200) { last_blink_tick = HAL_GetTick(); blink_on = !blink_on; TM1652_SetDisplayOn(blink_on); } }

这种“数值刷新 + 独立闪烁控制”的分离设计,让我不用在闪烁时反复构造段码数据,代码逻辑也清晰不少。

4.5 非阻塞发送下的刷新节奏问题

用中断或DMA发送时,有一个细节容易坑人:如果主循环里高频调用TM1652_DisplayString,而tx_busy标志还没清零,发送函数就会卡在while(tm1652_tx_busy)里面等待,这实际上又变成了阻塞。

解决思路有两种。一种是我前面代码里的写法,靠驱动内部的“串行化等待”保证同一时刻只有一帧在发送,代价是调用方可能等待最多2毫秒(9600波特率下5字节约5毫秒)。另一种是应用层主动控制刷新频率,保证两次显示更新的间隔大于发送耗时。我在秒表例子里把更新频率控制在50Hz,也就是20ms一次,远大于发送耗时,实测没有出现因为等待造成的功能卡顿。

具体选哪种,取决于项目的实时性要求。如果主循环周期在微秒级,那应用层的刷新频率控制更合适;如果主循环本来就松散,驱动内部等2毫秒无伤大雅,那串行化等待实现起来更省事。

5. 实测中的三个坑与完整排查链路

5.1 坑一:上电后显示乱码或者完全不亮

现象:程序下载后,TM1652完全没有反应,或者上电瞬间偶尔闪出一些毫无规律的段码。

排查链路:

第一步,用逻辑分析仪或示波器挂在PA9引脚上,观察复位后的波形。能看到波形说明MCU侧发送是正常的,看不到波形就要检查CubeMX里引脚复用是否正确,或者程序是不是卡在了某个地方——比如MX_USART1_UART_Init之前就死循环了。

第二步,确认TM1652的供电电压。数码管驱动芯片的VCC如果低于手册要求,内部逻辑可能处于不稳定状态。我遇到过一批芯片,标称3.3V供电,实际最低稳定电压接近4.5V,3.3V下显示就是乱码。如果芯片手册明确支持3.3V,那还要检查PCB走线上的压降——数码管段电流大的时候,地线上的压降可能把有效供电电压拉到阈值以下。

第三步,确认复位时序。主控上电后立刻初始化UART,立刻发送显示数据,TM1652可能还没来得及完成内部上电复位。稳妥做法是在TM1652_Init开头加一个50ms延时,先清屏,再设亮度,最后再显示数据。我后来所有项目里都保留了这个上电延时,再没遇到过乱码问题。

修复代码很简单:

void TM1652_Init(void) { HAL_Delay(50); // 等TM1652完成内部上电复位 TM1652_Clear(); // 清屏,确保显示缓存为空 TM1652_SetBrightness(3); // 默认中等亮度 }

5.2 坑二:数字错位,“1234”被显示成“2341”

现象:发送的四位数据,显示位置上整体偏移了一位,或者某一位的数据跑到了隔壁位上。

排查链路:

第一反应通常是组帧地址错了,改来改去解决不了。重新翻数据手册才发现,TM1652在不同批次里指令地址定义有细微差别,有的用低4位作为地址,有的要求先发一条“数据显示模式”命令再发地址。如果驱动代码里少了这一条模式命令,显示缓存就处于未激活状态,后续数据就可能被分配到错误的位地址上。

第二步,用排除法做“最小化验证”:只给0号位发送一个数字8的段码,观察它显示在哪一位;再给1号位发送,继续观察,逐个确认地址映射。经过这一步,会得到一张“实际地址表”,按这张表修正驱动代码里的地址枚举,问题基本就解决了。

第三步,检查数据字节的段序。如果数字显示出来形状不对,比如2显示成5,那就说明段位映射和芯片手册不一致。仍然用“全亮点亮、逐段断亮观察”的方式,确定a到g以及DP分别对应数据字节的哪几个bit,重新整理段码表。这一步花不了几分钟,但能避免无休止的无效修改。

这个坑的本质,是“芯片手册和实际板子批次不一致”。TM1652这类芯片的资料更新很频繁,PDF里偶尔还有翻译错误,最可靠的调试工具不是手册,而是逻辑分析仪加一个可控的测试程序。

我把排查方法总结成一张表,方便对照:

现象优先怀疑点验证手段
上电无反应供电电压或复位时序示波器查VCC波形,加延时
全部显示乱码UART波特率或电平匹配逻辑分析仪抓TX波形,核对帧格式
数字错位地址映射错误单字节逐位写入测试
数字形状不对段码表错误全亮后逐段断亮观察

5.3 坑三:USB转串口发数据正常,STM32固件发数据偶尔错帧

现象:用USB转串口模块直接连TM1652测试,怎么发都正常;一旦接上STM32的固件发送,偶尔出现某一位数字跳变。

排查思路:

先确认STM32的TX引脚和TM1652输入引脚之间的电平匹配。STM32的GPIO输出阻抗很低,驱动能力完全够,但如果在TX线上串联了电阻,而TM1652输入端的上下拉电阻配置不当,芯片内部阈值附近的电平可能产生毛刺,导致误判。我在一个项目里给TX串了100欧电阻,反而出现了错帧,去掉串阻后恢复正常。如果必须保留串阻用于EMI抑制,串阻值要尽量小,并且配合对地电容做滤波。

再确认波特率误差。CubeMX配置完成后,留意“USART1 9600 bps”这个参数旁边的Error百分比。如果误差超过1%,建议调整系统时钟频率或者换一个波特率档位。F103在72MHz主频、8MHz外部晶振下,9600的误差通常是很小的,基本都在0.1%以内。

最后确认接地。TM1652模块和STM32板之间必须共地。如果模块独立供电但没有和MCU共地,TX线上的逻辑电平参考点就不一致,会产生随机错帧。这种情况最隐蔽,示波器单端探头还看不出来,因为MCU侧的波形是完美的,但TM1652接收端看到的高低电平阈值已经偏移了。

这三个坑排查下来,我最大的体会是:任何“偶尔出现”的问题,八成不是软件逻辑错误,而是时序或电平问题。不要在代码层面反复打补丁,先用示波器或逻辑分析仪把物理层波形看清楚,很多问题一眼就能定位。

整套方案做下来,我对UART驱动TM1652这套链路算是彻底摸透了。最有价值的一点是,显示刷新从“占CPU的周期性任务”变成了“几字节串口数据”,省出来的时间和IO不仅能优化其他功能,还能让代码结构变得更清爽。如果你正在做的是四位数码管显示的面板类项目,又想从IO口扫描的泥潭里跳出来,这套方案值得直接参考。后面如果继续做多片级联和更复杂的滚动显示,我再整理一篇DMA发送的进阶内容。

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

用COCO格式气球数据集在MMDetection中快速跑通Mask R-CNN

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

作者头像 李华
网站建设 2026/9/28 1:19:00

Trace32+Python实现嵌入式变量自动抓取与断言验证

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

作者头像 李华
网站建设 2026/9/28 1:18:37

GPS模块协议解析:NMEA 0183与UBX的对比、配置及工程实践

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

作者头像 李华
网站建设 2026/9/28 1:18:34

24V工业电源如何通过四级EFT群脉冲测试:器件选型与PCB布局实战

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

作者头像 李华
网站建设 2026/9/28 1:17:47

QuickRecorder 1.5.4 macOS录屏实战:窗口捕获、系统音频与参数配置全解析

简介&#xff1a;QuickRecorder 1.5.4 是一款面向 macOS 用户的开源屏幕录制工具&#xff0c;适合需要录制教学演示、软件操作、游戏画面或线上会议的人群&#xff0c;尤其对追求轻量、免驱、无广告的进阶用户友好。软件基于 ScreenCapture Kit 接口开发&#xff0c;支持屏幕、…

作者头像 李华
网站建设 2026/9/28 1:17:29

威纶通HMI断电保存实战:RW寄存器与宏指令协同设计

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

作者头像 李华