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

资讯详情

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

TM1640驱动芯片:原理、时序与移植实战

TM1640驱动芯片:原理、时序与移植实战 简介TM1640是一种常用的LED驱动控制芯片内置7段数码管驱动逻辑常见于数字时钟、仪表盘、计数器等小型电子设备。此份驱动源码基于STC15系列单片机编写面向正在学习单片机外设驱动或需要快速点亮数码管的嵌入式开发者压缩包共4个文件包括2个头文件与2个C源文件整体体积仅3KB结构紧凑、依赖少方便直接阅读和移植。已有1078人浏览学习对于几KB级别的例程来说关注度不低。代码虽小却按照实际项目流程实现了初始化、工作模式配置、显示数据编码、逐位发送、动态扫描和循环刷新等完整步骤能够帮助读者从底层理解TM1640的时序控制与I/O操作。拿到手后既可按需调用驱动函数完成数码管显示也可在现有框架上扩展秒表、温度、电压采集等应用是嵌入式入门和电子制作中实用的参考代码。 最近整理移动硬盘翻出一个积灰很久的压缩包——TM1640驱动程序.rar。跟朋友说起这个文件他第一反应是Windows驱动的数字签名问题其实完全两码事这个“驱动”是单片机工程里的驱动代码包用来驱动一颗叫TM1640的LED控制芯片。TM1640是天微电子经典的16位LED驱动芯片两线串行接口IO占用极少一颗芯片就能带起八位数码管还带按键扫描。无论做电子钟、温湿度计还是仪表盘、小家电面板这颗芯片出场率都非常高。这篇文章我就把TM1640从原理到代码再到调试踩坑完整聊一遍拿到这个压缩包但不知道怎么用的朋友收藏这一篇就够了。1. TM1640到底是什么一颗把“扫描”外包出去的LED驱动芯片1.1 16位驱动能力到底能接多少东西TM1640内部集成了移位寄存器、锁存器和驱动电路引脚上分为GRID1~GRID8和SEG1~SEG16两组。这里的“16位”指的就是16个SEG段输出引脚配合8个GRID位输出引脚最常见的用法是驱动8位共阴数码管16个SEG分成两组一组接数码管的a~g和dp段另一组可以再接8个独立LED或者第二排段选8个GRID则负责逐位选中数码管。这颗芯片的方便之处在于动态扫描完全由内部硬件完成MCU只需要往显示寄存器里写数据芯片自己就会按顺序刷新每一位不需要你在中断里手动做“位选段选延时消隐”。实际项目中有人用它做电子钟的6位时间显示有人用它做仪表的8位数据面板还有人把它当成LED点阵的列驱动。对大多数应用来说一颗TM1640加一个MCU就能覆盖很多人机交互显示需求。1.2 为什么不是直接GPIO扫描也不是74HC595刚学单片机的时候我也是用定时器中断一位一位扫数码管。代码写起来不复杂但4位以上就头疼刷新率要够高才不会闪每位点亮时间又有限亮度上不去主循环一忙还容易卡顿。用TM1640之后MCU花几十微秒把数据写进芯片剩下时间全部可以干别的这个体验差别是巨大的。和74HC595相比TM1640的优势更明显。HC595虽然也能级联但它只解决“扩展IO”的问题按键扫描、亮度调节、显示驱动这些统统要自己写。TM1640用两根IO就完成了显示刷新、亮度调节、按键扫描一整套功能硬件BOM更少软件工作也更集中。方案占用IO动态扫描按键扫描亮度调节MCU直接扫描8~13个需要定时器无靠延时调74HC5953个需要软件配合无靠限流电阻TM16402个芯片内部完成支持命令字控制这张表基本说明了我为什么在大量项目里选TM1640省IO、省时间、省代码。2. 驱动程序的底层逻辑两线时序与命令字2.1 见到CLK/DIN别急着套I2C外设TM1640的通信引脚是CLK和DIN看起来跟I2C很像实际协议却不一样。最大的区别有两处一是数据传输顺序TM1640是低位先发LSB first而标准I2C是高位先发二是TM1640没有I2C那种从机地址和ACK应答机制命令格式完全是芯片自己定义的。所以直接用STM32的硬件I2C外设去驱动TM1640通常会非常别扭GPIO模拟时序反而是最稳、最容易移植的思路。时序要点可以拆成三步理解起始信号是CLK为高电平时DIN从高电平跳变到低电平之后每来一个CLK上升沿DIN上的1位数据被芯片采样结束信号是CLK为高电平时DIN从低电平跳变到高电平。实际调试时我习惯把CLK周期放到1μs以上也就是时序频率控制在1MHz以下这个速度对绝大多数显示刷新需求都足够还能避免信号质量差导致的数据错乱。提示TM1640的数据是低位先发这是移植时最容易踩的坑。如果你用标准I2C的习惯去写高位先发出去显示数据会整体“翻转”数码管出来的字完全是乱的。2.2 三条命令控制一台八位数码管TM1640的操作命令可以归纳为三类数据命令设置、地址命令设置和显示控制命令。数据命令设置里0x40最常用含义是“写数据到显示寄存器地址自动加1”。0x44表示固定地址写适合只更新某一位显存时使用。地址命令从0xC0到0xC7对应8个显示寄存器的起始地址。显示控制命令从0x88到0x8F其中0x88是关闭显示0x89到0x8F亮度逐级增加。完整刷新一次八位数码管的流程是这样的发起始信号发送0x40发送结束信号再发起始信号发送0xC0然后连续发送8个字节的段码数据最后发起始信号发送0x8F开显示发结束信号。因为启用了地址自动加1模式8个段码字节发完后内部地址指针会自动走到下一个寄存器不需要中途再发地址效率很高。2.3 段码表为什么和共阴共阳强相关数码管的8段a~g、dp在段码表里每一位对应一个段。共阴数码管的公共端接GND点亮某一段时对应SEG引脚要输出高电平所以数字0的段码是0x3F也就是二进制00111111a到f亮g和dp灭。共阳数码管则相反数字0的段码是0xC0。如果模块厂家用的是共阳数码管而你按共阴段码表驱动显示结果就是“取反”效果数字0会变成中间一横数字8会变成中间一竖不亮一眼就能看出来。拿到模块的第一步先确认极性再选段码表这个顺序不能反。另外有些模块的PCB走线会把SEG引脚和数码管段位顺序打乱比如SEG1接的不是a而是b这时段码表无论怎么调都是错位的必须按照实际接线做一张“重映射表”把标准段码的每一位重新排列后再写入。3. 拿到压缩包之后工程移植与实战3.1 rar解压之后你通常会看到这些文件一个典型的“TM1640驱动程序.rar”解压之后一般包含TM1640.c和TM1640.h两个核心驱动文件一个main.c演示程序一份接线说明或者原理图外加一颗芯片的数据手册PDF。有些店家提供的工程是Keil C51平台的用的STC89C52有些给的是STM32标准库版本还有的用HAL库重写过。不管什么平台驱动代码的核心结构都差不多底层是GPIO引脚操作往上是起始/结束信号和写字节函数再往上是命令发送和显示接口。理解这个层级关系之后移植就变成很机械的事把引脚宏定义改掉把延时函数换成目标平台的延时然后确认数据位序和命令字没错基本就能跑起来。千万不要copy整个工程编译那样光环境配置就能折腾半天。3.2 移植到STM32从引脚宏到显示函数的完整过程我自己最常用的移植方式是把TM1640的CLK和DIN接到两个普通GPIO配置为推挽输出。比如PA0接CLKPA1接DIN在头文件里定义好引脚操作的宏#define TM1640_CLK_H() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET) #define TM1640_CLK_L() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET) #define TM1640_DIN_H() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET) #define TM1640_DIN_L() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET)然后实现起始信号、结束信号和写字节的底层函数。写字节是最核心的部分需要注意低位先发以及CLK上升沿采样static void TM1640_Start(void) { TM1640_CLK_H(); TM1640_DIN_H(); delay_us(2); TM1640_DIN_L(); delay_us(2); TM1640_CLK_L(); } static void TM1640_Stop(void) { TM1640_CLK_L(); TM1640_DIN_L(); delay_us(2); TM1640_CLK_H(); delay_us(2); TM1640_DIN_H(); } static void TM1640_WriteByte(uint8_t dat) { uint8_t i; for (i 0; i 8; i) { TM1640_CLK_L(); if (dat 0x01) { TM1640_DIN_H(); } else { TM1640_DIN_L(); } delay_us(1); TM1640_CLK_H(); delay_us(1); dat 1; } }这三个函数就是整个驱动的“地基”。我见过不少朋友移植失败最后查出来就是引脚宏写反了或者把低位先发写成了高位先发仅此而已。3.3 显示接口与段码表让数码管说出人话底层的字节发送搞定之后显示接口就顺理成章了。先准备一张共阴段码表const uint8_t seg_code[] { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71 };这张表对应0~9、A、b、C、d、E、F。然后封装一个显示函数把8字节数据显示缓冲整体刷到芯片里void TM1640_Display(uint8_t *buf, uint8_t len) { uint8_t i; TM1640_Start(); TM1640_WriteByte(0x40); // 数据命令地址自动加1 TM1640_Stop(); TM1640_Start(); TM1640_WriteByte(0xC0); // 起始地址 for (i 0; i len; i) { TM1640_WriteByte(buf[i]); } TM1640_Stop(); TM1640_Start(); TM1640_WriteByte(0x8F); // 开显示最大亮度 TM1640_Stop(); }主程序里我习惯维护一个全局的显示缓冲数组业务逻辑只修改数组内容然后调用TM1640_Display把整组数据刷进去。比如要显示“12345678”就依次把seg_code[1]、seg_code[2]……填到缓冲里。如果要显示带小数点的数字就把对应段码加上0x80点亮dp段。这种方式代码结构清爽以后换驱动芯片时改动面也很小。4. 常见问题与排查技巧实录4.1 数码管不亮或者乱码按顺序查这几处拿到模块点不亮是最常见的情况。我自己的排查顺序非常固定先量供电TM1640工作电压范围和MCU是否一致数码管全亮时电流可能到一两百毫安电池供电或者USB供电都要确认带载能力再查极性共阴共阳接反的典型表现就是段码“取反”然后检查代码里有没有发送显示控制命令很多人只发了数据命令忘了发0x89~0x8F这一组芯片默认关显示自然什么都不亮。最后查时序重点看CLK频率和DIN数据在CLK上升沿附近有没有变化如果CLK太快可以把每一小步的延时从1μs加大到2μs甚至5μs降速到几十kHz基本能解决大多数采样错乱问题。5V单片机和3.3V模块混接时还要确认IO逻辑电平是否兼容必要时加电平转换或者串联限流电阻。4.2 亮度不均匀和闪烁的原因TM1640的显示控制命令0x89到0x8F亮度是逐级递增的0x88是关闭显示。如果觉得整体亮度不够先不用急着改硬件把这个命令字往上调一档试试。但有时候调最大还是暗就要考虑限流电阻和电源了。数码管每个段的工作电流一般在几毫安到十几毫安8位全亮时总电流很可观供电模块如果电流不够电压跌落就会导致亮度变暗甚至闪烁。闪烁还有一个容易被忽略的原因显示刷新函数被放在了某个长时间阻塞的主循环里比如按键等待或者延时子程序运行时数码管长时间不刷新。其实TM1640内部一直在自己扫描所谓闪烁很多时候是数据“时有时无”造成的错觉。解决方法是把TM1640_Display放到定时器里周期性调用或者确保主循环的每次循环周期都足够短。硬件上建议在TM1640电源脚附近放一个0.1μF陶瓷电容和一个10~100μF电解电容滤掉电源纹波显示会更稳定。4.3 按键扫描功能和显示刷新别互相干扰部分TM1640模块保留了按键扫描能力可以接小规模按键矩阵。我在项目里用过这个功能最大的感受是按键读取一定要有固定的采样周期不能和显示刷新同时塞在同一个循环里随意调用。扫描太频繁键值抖动过滤不过来容易误触发扫描太慢按键响应又迟钝。一般5~10ms读一次按键再做一次去抖判断手感就比较正常。如果按键扫描和显示写显存用了同一条通信线更要小心两者不能“同时”操作。我踩过的一个坑是按键扫描过程中插入了显示刷新导致通信时序被中断芯片内部状态错乱显示出现毛刺。后来我在所有TM1640接口函数外面加了一个简单的临界区保护或者确保两个功能在同一个任务里顺序执行问题就消失了。4.4 问题排查速查表现象可能原因排查/解决方向所有数码管不亮没发显示控制命令、供电异常检查0x89~0x8F是否发送万用表测VDD显示乱码/取反共阴共阳接反、数据位序错换段码表检查LSB first是否实现数字错位地址写入错误、显示缓冲长度不够检查0xC0起始地址确保8字节对齐亮度低亮度等级没调够、限流电阻偏大把0x8F写入适当减小电阻显示闪烁刷新函数被阻塞、电源纹波大定时器周期刷新加去耦电容按键乱跳去抖不足、扫描周期太短5~10ms周期读键做滤波这张表我贴在很多项目的调试记录里每次排查TM1640相关的显示问题就直接对着看能省不少时间。最后分享一个我用TM1640的个人经验一定不要把显示刷新和按键处理放在同一个层级里连续调用。我习惯把显示缓冲做成全局数组业务逻辑只改数组内容然后在一个固定的短延时任务里调用TM1640_Display这样不管业务逻辑多复杂数码管始终稳定。你手里那份rar如果没有注释或者注释不全也别急对照数据手册把命令字逐条过一遍理解之后自己也能重写一套驱动。这颗芯片便宜、皮实值得好好用它。本文还有配套的精品资源点击获取
返回列表