计数器技术全解析:从51单片机、FPGA到PLC的工程实践与项目复盘
计数器这名字听着不起眼但干过嵌入式、工控或者数字电路这行的人都知道它几乎是所有逻辑控制的地基。学生时代搞51单片机跑秒表工作以后做产线统计、车位检测绕来绕去都离不开“计数”这件事。很多刚入行的朋友觉得计数器不就是数个数嘛可等真正动手从555搭出来的硬件计数电路到FPGA里一行Verilog写出的模值计数器再到西门子S7-1200和三菱GX Works2里面的计数器指令每个场景的坑都不太一样。这篇我就把自己在硬件计数、单片机定时器、FPGA计数器、PLC计数器这几个方向上的实操经验串起来聊一聊最后还会复盘易拉罐计数和停车场车位计数这两个典型项目把选型思路、关键代码和排查技巧一次性说清楚。不管你是刚摸开发板的新手还是在产线上调试的老手都应该能从里面找到点可以直接用的东西。1. 计数器到底是什么三个层面理解它1.1 从硬件电路的角度看计数器的本质是一组能保存状态的触发器Flip-Flop每来一个有效沿状态就按照预定规律变化一次。我当时第一次在数字电路实验课上搭4位二进制计数器用四个D触发器和门电路折腾了一下午焊完才意识到计数器不是一个“会加一”的黑盒子而是一个有记忆功能的时序逻辑电路。它必须有时钟输入、复位输入还要决定是加法、减法还是可逆计数。这个基础认知很重要因为后面不管是看Verilog代码里的cnt cnt 1还是看PLC里的CTU指令底子都是同一套东西。1.2 从单片机/软件的角度看到了单片机层面计数器更多时候被包装成“定时器/计数器”外设。同一个硬件模块内部其实就是一个不断累加的寄存器只不过给它提供计数脉冲的时钟源有两种一种是内部的机器周期或分频时钟这时它叫定时器另一种是外部引脚输入的脉冲这时它才真正叫计数器。刚学51单片机的时候很多人都搞混定时和计数其实只要记住一句话定时器数的是内部时钟的周期计数器数的是外部事件的发生次数硬件结构几乎一样区别只在时钟来源。1.3 几个绕不开的关键参数不管用什么平台实现计数器有几个参数永远要心里有数计数模值计数到多少回绕或者停止。比如模10计数器数到9就归零模65536的16位计数器数到65535回零。初值从哪个数开始数。51单片机定时器常用“重装初值”的方式计算出要定时的时间再反推初始值。计数方向加法、减法还是可逆。PLC里会有CTU、CTD、CTUD之分。使能和复位能不能暂停计数以及什么时候清零。这条在工程上特别关键尤其是产线统计复位时机没处理好统计数据就是一笔糊涂账。可以这么类比计数器就像操场跑圈用的计圈器每到一圈按一下按键寄存器相当于表盘上显示的数字。定时器则是计圈器接到了固定节拍器上每响一声加一。搞懂了本质换到哪个平台都只是变个写法而已。为了更直观把常见几类计数器方案放在一起对比实现方案典型器件/平台计数速度主要优势主要局限硬件计数器555产生脉冲 CD4017取决于输入脉冲频率纯硬件响应快无需编程逻辑固定改一次模值要改电路单片机计数器51的T0/T1等一般不超过系统时钟的几分之一配置灵活能配合显示和通信软件处理中断有一定延迟FPGA计数器Verilog RTL可以做到很高受时钟约束并行处理、时序精确可控开发门槛较高成本相对大PLC计数器S7-1200、三菱FX系列等普通计数取决于扫描周期工业环境稳定梯形图直观高速场景需用高速计数模块2. 硬件计数器实战555定时器加CD40172.1 555到底是计数器还是振荡器先说清楚一个容易被误解的点NE555本身不是计数器芯片它是个定时/振荡芯片。网上经常说“555计数器”其实完整方案是“555产生计数脉冲后面再接一个计数芯片”。555在这里负责提供稳定的脉冲源常见用法是构成多谐振荡器输出一个方波。要让这个方波的频率准确就得会算它的充放电时间常数。2.2 一个经典的脉冲计数电路我当时做过一个纯硬件的产品计数演示结构很简单NE555接成多谐振荡器输出连续方波作为计数时钟。CD4017作为十进制计数器输出端Q0到Q9依次出现高电平表示“数到几”。用一排LED接在CD4017的Q输出端直观看到计数状态变化。想复位清0把CD4017的MR复位引脚接一下高电平就行。这个电路适合给纯硬件入门的同学练手因为每个芯片功能单一出问题也好排查。一般排查顺序是先拿示波器看555的OUT脚有没有方波再看CD4017的时钟引脚有没有信号最后看Q输出逻辑对不对。很多时候不是芯片坏了而是面包板接触不良或者复位引脚悬空乱跳。2.3 关键参数计算和选型NE555多谐振荡器的频率计算公式是f 1.44 / ((R1 2 * R2) * C)这里的R1是连接VCC和放电脚的上电阻R2是充放电共用的电阻C是定时电容。我实际想要一个大约1Hz的慢速脉冲做演示选了R11kΩR2680kΩC1μF代入公式f 1.44 / ((1k 2 * 680k) * 1μF) ≈ 1.44 / 1.361 ≈ 1.06Hz实测输出周期约0.94秒和计算基本吻合。需要注意公式里的电阻单位要和电容单位换算正确千万不能把兆欧和微法直接相乘后不换算我第一次算就是因为单位搞混算出个完全离谱的值。实际搭建时定时电容尽量用CBB电容或钽电容普通电解电容漏电大频率会漂实测能差百分之十几。2.4 纯硬件计数电路的工程局限这类纯硬件计数方案虽然直观但在真实产线上很少单独用。原因有二一是计数结果要显示的话CD4017只能配合LED或数码管数据不好远传二是没有存储和判断能力比如“数到100就停机”这种逻辑需要再加比较器和触发器电路会变得很复杂。所以工程上更常见的做法是用传感器产生的脉冲直接送给单片机或PLC的计数器引脚由程序或梯形图完成后续判断和输出。3. 51单片机定时计数器讲解从寄存器到实际配置3.1 定时器和计数器在硬件上其实是一家51单片机经典的定时/计数器T0和T1内部都是16位加1计数器核心寄存器是TH0/TL0或TH1/TL1。它们的工作模式由TMOD寄存器决定启动控制在TCON寄存器里。当TMOD里的C/T位为0时计数器数内部机器周期这就是定时模式当C/T位为1时数外部引脚T0或T1输入的脉冲这就是计数模式。GATE位的用途也值得一提它可以控制计数只在外部INT引脚为高时进行用来测量某个外部信号的持续时间很实用。3.2 定时器初值的计算过程以最常用的模式116位定时器为例假设晶振12MHz机器周期就是1μs我要定时50ms那要计数的机器周期个数是50000。16位计数器的最大计数值是65536所以初值就是65536 - 50000 15536换算成十六进制是0x3CB0。程序里就写成TH0 0x3C; TL0 0xB0;这里容易犯的毛病是只给TH0和TL0赋初值却忘了在中断里重装初值结果第一次定时准后面全都变成65.536ms了。所以我习惯在中断服务函数开头直接重新给TH0和TL0赋值保证每次溢出后都从同一个起点开始数。3.3 一个可复用的51定时器代码示例下面是一段我实际调试过的定时器0初始化代码定时50ms中断里翻转LED可以当模板直接用#include reg51.h sbit led P1^0; void Timer0_Init(void) { TMOD 0xF0; // 清除T0的模式位 TMOD | 0x01; // 设置T0为模式116位定时 TH0 0x3C; // 50ms定时初值高8位 TL0 0xB0; // 50ms定时初值低8位 ET0 1; // 允许T0中断 EA 1; // 打开总中断 TR0 1; // 启动定时器0 } void Timer0_ISR(void) interrupt 1 { TH0 0x3C; // 中断里重新装初值 TL0 0xB0; led !led; // 翻转LED周期为100ms } void main(void) { Timer0_Init(); while (1); }这段代码的关键在于TMOD的赋值方式。我用了先清后或的方式避免影响T1的配置这种写法在同一个程序里同时使用T0和T1时特别有用不容易互相覆盖。3.4 用T0做外部脉冲计数把C/T位改成1T0就变成计数器了。外部脉冲从P3.4T0引脚输入每来一个下降沿计数器加1。使用前有个细节要注意外部脉冲的宽度和频率必须满足单片机的采样要求通常是至少保持若干个机器周期频率太高会漏计数。此时TH0和TL0的值就是实际的脉冲个数读取的时候需要先读TH0再读TL0否则可能读到“半个”变化过程中的乱值。晶振频率偏差是定时不准的常见根源。如果用12MHz晶振实际频率可能是11.996MHz一天积累下来误差挺可观。工程上需要精准定时时我会用外部有源晶振或者GPS授时模块对齐但做普通计数统计这个误差完全在可接受范围。4. Verilog计数器的设计思路与RTL实现4.1 FPGA里的计数器为什么无处不在在FPGA设计里计数器不只是“数数”这么简单。几乎所有的时序逻辑都需要计数器参与分频产生各种时钟、状态机跳转计时、串口波特率生成、数码管扫描刷新本质上都是计数器的应用。所以verilog计数器是FPGA入门必须吃透的内容它考察的是你对时钟沿、复位方式、位宽溢出这几个基本概念的掌握程度。4.2 一个带异步复位的模10计数器下面这段是典型的同步时序逻辑写法模10计数器带异步复位和使能module counter_10 ( input wire clk, input wire rst_n, input wire en, output reg [3:0] cnt ); always (posedge clk or negedge rst_n) begin if (!rst_n) cnt 4d0; else if (en) begin if (cnt 4d9) cnt 4d0; else cnt cnt 1b1; end end endmodule这里面有几个新手特别容易踩的点。第一复位用rst_n这种低电平有效信号时条件判断里必须写!rst_n而不是rst_n写反了复位失效上板跑起来就是未知状态。第二比较条件用的是cnt 4d9不要图省事写成cnt 10因为cnt位宽只有4位10根本表示不了综合器会告警。第三非阻塞赋值在时序逻辑里是标配不能在同一个always块里混用阻塞赋值否则仿真和实际行为会不一致。4.3 从计数器到分频器的演进计数器最常见的应用之一就是分频。想得到时钟的偶数分频直接对计数器溢出位取反就行。比如系统时钟50MHz我需要1MHz的时钟那就是50分频计数值从0加到24翻转一次再从25加到49翻转回来。代码如下module clk_div_50 ( input wire clk, input wire rst_n, output reg clk_out ); reg [5:0] cnt; always (posedge clk or negedge rst_n) begin if (!rst_n) begin cnt 6d0; clk_out 1b0; end else begin if (cnt 6d49) begin cnt 6d0; clk_out ~clk_out; end else begin cnt cnt 1b1; end end end endmodule这个代码我建议初学者自己手动跑一遍仿真重点看clk_out的翻转周期是不是正好是clk的50倍。实际调试中还可以用逻辑分析仪抓引脚波形光靠仿真有时候看不出竞争冒险问题上板实测更靠谱。4.4 上板调试的独家体会做FPGA计数器项目时最容易出现“仿真对了上板不对”的情况。原因大多是异步信号没有处理外部按键直接作为复位或使能信号在FPGA内部会产生亚稳态。解决办法很标准就是把外部信号经过两级D触发器打拍同步后再用也就是常说的“双触发器同步器”能极大降低亚稳态概率。另外计数器的位宽一定要算准预估最大计数值是多少就留出足够的二进制位不然计到一半溢出了逻辑直接乱掉。5. PLC计数器深度解析西门子S7-1200与三菱GX Works25.1 西门子S7-1200计数器说明S7-1200的计数器指令在TIA Portal里以功能块的形式提供常见的三种是CTU加计数、CTD减计数和CTUD加/减计数。与老款S7-200的计数器不同S7-1200的IEC计数器需要分配一个背景数据块用来保存计数器的当前值和内部状态。刚开始从200转1200的人经常问为什么我拖一个CTU出来会提示我选DB块其实就是这个原因。以CTU为例关键参数有CU计数脉冲输入端每个上升沿计数值加一。R复位端为1时CV清0。PV预设值达到这个值后Q输出为1。CV当前计数值。Q计数输出CV PV时导通。这种“当前值预设值复位端”的结构是PLC计数器最通用的框架了解一个品牌后换到其他品牌只是名字和调用方式略微变化。5.2 S7-1200计数器在程序里怎么用在TIA Portal里新建一个CTU指令后我一般会这样配置在FC或FB里调用CTU背景DB命名为“Counter_Product”CU接一个传感器信号“Sensor_Pulse”R接复位按钮“Reset_Btn”PV设为100Q接一个输出线圈“Full_Lamp”。当传感器检测到100个脉冲后Full_Lamp置1表示达到设定数量。如果你习惯用SCL也可以直接调用IEC计数器块Counter_Product(CU : Sensor_Pulse, R : Reset_Btn, PV : 100, Q Full_Lamp, CV Current_Value);需要注意普通计数器指令受PLC扫描周期限制输入信号的频率不能太高。如果现场传感器脉冲很密集比如高速产线每秒几十上百个脉冲必须使用S7-1200的高速计数器HSC功能通过组织块中断和工艺对象来处理普通CTU会漏计很多。5.3 三菱PLC计数器C的用法与GX Works2配置三菱FX系列PLC的计数器用C编号分16位增计数和32位增/减计数两大类还有一类高速计数器专门接高速输入端子。GX Works2里配置计数器的思路和西门子略有不同三菱PLC的计数器编号自带“保持型/非保持型”属性比如C0到C199是非保持型断电归零C200到C234是保持型断电后能记住当前值。做产线表单统计时一定要选保持型否则突然停电当前计数值全丢。在GX Works2里写计数器梯形图典型的两行逻辑第一行X0作为计数脉冲输入驱动线圈C0。第二行C0的常开触点驱动Y0输出表示计数到达。比如X0是一段检测传感器C0的设定值在指令里写成K100当X0接通次数达到100次时C0触点动作Y0得电。执行这段逻辑时三菱PLC是按扫描周期刷新普通计数器的所以同样不能用于超高速输入。要做高频计数必须用C235等高速计数器端子配合程序里的SPD指令或DHSCS指令读取。这里要提一个坑三菱的普通计数器指令在目标值到达后即使脉冲继续来计数值保持不再增加。如果现场想达到目标后继续累计需要额外用别的计数器或改用比较指令。这个行为特点和西门子CTU的“到目标值后继续增加”不太一样跨品牌做项目时要格外留意。5.4 用PLC计数器实现产线产品统计我做过一个最简单也最实用的产线统计逻辑传送带上有光电传感器每过一个产品就输出一个脉冲PLC计数达到设定数量后声光报警提示换箱。整个逻辑的核心就是传感器信号接到PLC输入点计数器指令累加当前值送到触摸屏显示达到预设值触发输出。这个方案稳定可靠触摸屏还可以随时修改预设数量比纯硬件计数器灵活太多。做这类项目时比较花时间的往往是传感器安装位置和抗干扰处理比如光电传感器的灵敏度调整、屏蔽线接地这些物理层面的细节对计数准确度影响极大。6. 两个热门应用场景完整拆解6.1 易拉罐计数器项目方案易拉罐计数的需求很典型生产线上易拉罐快速通过统计数量达到设定装盒数量时通知工人或驱动挡板动作。我之前做过一个以PLC为核心的方案传感器选的是对射式光纤传感器因为易拉罐是金属反光面用漫反射光电容易误判用对射光纤检测稳定很多。传感器输出接到PLC高速计数输入端子PLC内部做计数、比较和驱动输出。如果不用PLC用单片机也能实现类似功能但要注意信号抖动问题。易拉罐经过时传感器输出不是理想的方波会有多次抖动直接计数会多计。解决办法有两种硬件上用RC低通滤波或施密特触发器整形软件上做延时消抖比如检测到下降沿后延时10ms再采样确保同一个产品只计一次数。STM32等单片机实现起来也不难外部中断加定时器消抖计数器记录脉冲个数最终显示在OLED屏幕上。这里的关键参数是传感器响应时间。产线速度很高时两个易拉罐之间的时间间隔极短传感器响应太慢会丢包。选型时要算好假设产线每秒过5个易拉罐两个罐体间隔最小200ms那传感器的响应时间至少要在20ms以内实际留2到3倍余量用响应时间小于10ms的传感器比较稳妥。计数频率越高对控制器接口的要求也越高普通单片机外部中断在低频时没问题几十千赫以上就要考虑硬件计数器或专用计数芯片了。6.2 基于32的简易停车场车位计数器停车场车位计数器是热搜词里比较真实的需求。做一个基于STM32的简易停车场系统最常见的检测方案是地感线圈和红外对射。地感线圈准确率高但要切割地面施工成本大红外对射安装简单适合做演示和简易改造。我做过一个基于红外的方案每个车位入口装一对对射式红外传感器车辆经过时遮挡光线传感器输出电平变化STM32检测到下降沿/上升沿就更新车库剩余车位数剩余车位显示在数码管或LCD上。用STM32做的核心逻辑可以概括为// 伪代码外部中断检测车辆进入/离开 void EXTI_IRQHandler(void) { if (进入方向传感器触发) { if (剩余车位 0) 剩余车位--; } if (离开方向传感器触发) { if (剩余车位 总车位) 剩余车位; } }实际做到能稳定运行比这个伪代码要复杂得多。首先是传感器信号的消抖红外对射在车辆遮挡瞬间会产生多次沿跳变不消抖就会一次过往计好几辆车。我当时的做法是在外部中断里记录时间戳小于200ms的重复触发直接忽略。其次是断电保持问题停车场不可能一直有人盯着断电后剩余车位数必须保存在STM32内部Flash或外部EEPROM里重新上电能恢复。第三传感器安装高度和对齐角度很有讲究装歪了或者阳光直射传感器会产生大量误报现场调试时我吃了不少亏。6.3 多方向计数和特殊情况处理现实场景比想象中复杂。比如易拉罐输送带可能有两路汇流停车场可能有进有出这些都是“双方向计数”问题。单一计数器只能加不能减所以需要两个计数器来的方向加一走的方向减一。如果用PLC实现可以用CTUD加/减计数器一个指令就搞定用单片机做就是两个外部中断分别处理加减逻辑。更麻烦的是“同一辆车倒车反复遮挡”的情况光靠两个传感器还不够需要逻辑判断触发顺序这就上升到状态机设计了。停车场车位计数做到后期我建议至少在入口和出口各放两个传感器通过传感器被遮挡的顺序判断真实进出方向能过滤掉大部分误判。7. 常见问题与排查技巧实录计数器项目出问题归纳起来其实就几类信号源、逻辑配置、干扰和掉电保存。下面这张表是我在实际调试中遇到的高频问题基本覆盖了从硬件到软件的排查方向。现象可能原因快速排查方法计数值乱跳没东西经过自己加传感器灵敏度太高、线路干扰检查屏蔽层是否接地调整传感器灵敏度用示波器看输入波形是否干净实际经过100个只计了80多个脉冲频率超过计数器上限换高速计数器模式或专用计数芯片检查扫描周期是否过长PLC计数器到目标值后不再累计该品牌计数器到达预设值后的行为查阅手册改用加/减计数器CTUD或比较指令完成后续统计51单片机定时器定时时间偏长/偏短初值没在中断里重装、晶振不准确认TH/TL是否在中断里重载用频率计校准晶振555电路没有输出脉冲定时电容漏电、供电不足用示波器量NE555的OUT脚检查VCC是否稳定在5V更换CBB电容Verilog仿真正常上板计数异常外部信号未同步、位宽溢出外部信号加两级D触发器打拍检查计数器位宽是否足够停车场车位断电后数据清零没有做掉电保存用Flash或EEPROM定期保存计数值上电时读取恢复易拉罐计数重复计传感器输出抖动增加软件消抖或硬件RC滤波、施密特触发器排查计数类故障我摸索出一个固定套路先看原始信号再看控制器的接收最后看逻辑处理和数据存储。比如计数值不对我第一件事永远是拿示波器或逻辑分析仪看传感器输出端的波形看脉冲个数是否和实际一致。很多时候问题根本不在程序而在传感器安装位置、信号线干扰或者电源纹波这些物理层面的东西没搞定软件怎么写都白搭。最后分享一点实际做项目的体会计数器项目看上去小真做好其实挺考验人的细节功底的。我个人的感受是做这类项目前一定先想清楚三个问题现场的计数频率是多少需不需要断电保存信号源会不会有抖动。这三个问题确定了方案基本就成型了。另一个实用习惯是无论用单片机还是PLC都先把计数功能单独调通再接显示、报警这些外围功能分步联调会省很多排查时间。做产线或者停车场的项目时数据准确性是第一位的计错了再好看的界面也没有用。以后如果再让我给新人推荐练手项目我一定还是首推计数器因为从纯硬件电路到Verilog到PLC它一条线串起了电子工程师需要的大部分基本功。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →