数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信登录

微信扫一扫,快速登录

搜索
查看: 552|回复: 15

[工仪] 分享一款脉宽测量仪的电路及源代码

[复制链接]
发表于 2025-8-4 11:09:57 | 显示全部楼层 |阅读模式
本帖最后由 刘佑红 于 2025-8-4 11:17 编辑

分享一款脉宽测量仪的电路及源代码
    因制作点焊机控制板,需要测量脉冲宽度,其中电容点焊机控制板的脉宽范围一般为1-50毫秒,变压器点焊机控制板的脉宽范围一般为0.1-0.5秒,最初我曾使用廉价的体育秒表改制,但失败了,后一直使用示波器测量脉宽。使用示波器测量二三十毫秒以下的脉宽倒很直观,但长脉宽波形就是流动不固定的了,时间就难估测准了。在今年6月初利用空暇之机,制作了一款脉宽测量仪,现将电路及源代码分享给朋友。
    一、电路原理图
    以上原理图是理我根据PCB版图反推的,我没有先做SCH图,而是直接做的PCB,但原理图应该没有问题。
    二、测量原理
    采用单片机定时计数功能来实现测量脉冲宽度,我使用的是STC8H1K08单片机,使用计时器0。STC单片机TMOD寄存器中GATE位置1的时候,INT0(P3.2)引脚只有在为高电平的时候,并且TR0置1的时候,定时/计数器才开始计数,P3.2引脚上的高电平消失,TH0和TL0也就停止计数了,在系统时钟为12MHZ,定时器12T模式下,1个计数就是1us,把TH0和TL0里面的数值读出来就知道脉宽是多少了。比如读出来TH0+TL0=50000,那么脉宽时间爱你时间就是50000us=50mS,然后通过数码管显示即可。
当TH0和TL0计数超过255时,计数器就会溢出归0,TH0和TL0最大计数就是65535,即最长测量时间65535us。如果记录TH0溢出次数,那么脉宽范围就是溢出次数*65536+TH0*256+TL0,溢出次数最大可到STC8H1K08单片机最大位32位,因测量需要和数码管位数限制,我的脉宽测量范围限制在999秒,其实太长的范围也没有必要。

    三、PCB图和源代码、HEX文件

    对比PCB图和SCH原理图,可以看出PCB上元件空位较多,原因是第一版设计时设计了负脉冲(低电平有效)脉宽测量模式和一键电子开关机功能,但设计出现了BUG,所以取消负脉冲测量模式和一键开关机功能。
    四、测量误差
    在采用12MHZ时钟频率,计数器12分频模式下,一个计数及时1us,在采用片内IRC模式(非外置晶振)时,写入代码12MHZ频率误差显示为0.1%,即频率精度为千分之一,理论上测量ms级的误差也应该接近这个精度,在测试电片机产生的ms级脉冲时,感觉总是偏小,每一ms偏小几个us,因为不知道脉冲源精度,并不清楚是脉冲源的原因还是测量的原因。第二版测试仪外置了12MHZ晶振,在测量时显示脉冲源每一ms的长实际多了2us,考虑到单片机定时中断产生脉冲,因中断的原因,时间应该会稍长些。于是便对采用内置IRC振荡频率模式的代码,加入了误差修正,即将测量结果稍稍增大一些,使显示结果更接近于实际。
对实际测量误差,因对单片机还处于一知半解阶段,不能分析出误差的具体大小,只能凭经验修正。对采用外置晶振的测量代码,就没有再进行修正,感觉已接近实际数值了,其误差我能够接受。
    五、其他说明
    测量仪采用3.7V单锂电供电,供电端加有防反接二极管和4.7V稳压管,供电接反不通电,误接高于4.7V的电压则稳压管短路,会烧坏稳压管或者防反接二极管,起到一定的防高压损坏后级电路作用。电路没有设计稳压IC,电池电压高于3.3V即可正常工作。
数码管显示第一位是H时,H后面显示数字单位为秒,第一位不是H是数字的话,后面显示数字单位为ms,如数码管显示1.004,即脉宽是1.004ms,显示50.10,脉宽是50.10ms,显示H16.5,脉宽就是16.5秒。
以下附件中,第一个是以上文档的PDF板,压缩包是hex文件。



  1. /************************测量脉冲宽度******************************

  2. STC8H1K08 TSSOP20  IRC内部时钟选12MHZ
  3. P37  个位    数码管位驱动0
  4. P54  十位    数码管位驱动1
  5. P34  百位    数码管位驱动2
  6. P35  千位    数码管位驱动3
  7. P32  into    脉冲输入端

  8. 硬件版本:适用20250613(无开关机即负脉冲输入功能)
  9. 软件版本:第一版12T12MHZ
  10. *****************************************************************/
  11. #include<stc8h.h>         //STC8H1K08 TSSOP20
  12. #include <intrins.h>      //调用_nop_函数

  13. typedef unsigned char u8; //对数据类型进行定义声明
  14. typedef unsigned int u16; //对数据类型进行定义声明
  15. typedef unsigned long u32;//对数据类型进行定义声明

  16. sbit Kset = P3^4;         //SET按键
  17. bit flag10ms = 0;         //10ms中断标志
  18. bit Err = 0;              //脉宽超时标志
  19. bit gengxin = 1;          //更新标志
  20. bit xianflag = 0;          //显示标志
  21. u8 ttH = 0;               //计数高8位
  22. u8 ttL = 0;               //计数低8位
  23. u16 ttyi = 0;             //定时器0计数溢出J计数
  24. u16 yi = 0;               //定时器0计数溢出次数
  25. u32 t = 0;                //脉宽计数

  26. /**********************数码管真值表-共阴0-9***************/
  27. u8 code LEDchar[10]={0xF9,0x81,0xBA,0xAB,0xC3,0x6B,0x7B,0xA1,0xFB,0xEB};
  28. u8 code LED_DDP[10]={0xFD,0x85,0xBE,0xAF,0xC7,0x6F,0x7F,0xA5,0xFF,0xEF};//加小数点
  29. u8 Ledbuff[4]={0xF9,0xF9,0xF9,0xF9}; //H=0xD3,E=0x7A,r=0x12

  30. /********************按键扫描*************************/
  31. void keycan()
  32. {
  33.   static u8 set = 0;        //按键扫描次数

  34.   if(flag10ms)             //10ms时间标志,由Tim2中断10次计数产生
  35.     {
  36.       flag10ms = 0;        //清零10ms标志
  37.       if(Kset) {set = 0;}  //按键按下过程中抖动或者弹起
  38.         else               //按键按下
  39.           {
  40.             set++;         //10ms扫描一次加1
  41.             if(set == 2)
  42.               {
  43.                 Err = 0;
  44.                 ttH = 0;
  45.                 ttL = 0;
  46.                 ttyi = 0;
  47.                 gengxin = 1;
  48.               }
  49.             }
  50.     }
  51. }

  52. /********************更新时间显示*************************/
  53. void LED()
  54. {
  55.   if(gengxin)
  56.     {
  57.       gengxin = 0;
  58.       t = ttH*256L + ttL + ttyi*65536L;//脉宽计数,时间为us
  59.       t = t*513/512;                  //误差校正
  60.       if(Err)  //超量程显示错误标志Err
  61.         {
  62.           Ledbuff[0] = 0x00;          //个位不显示
  63.           Ledbuff[1] = 0x12;          //十位显示r
  64.           Ledbuff[2] = 0x12;          //百位显示r
  65.           Ledbuff[3] = 0x7A;          //千位显示E
  66.         }
  67.         else  //正常量程
  68.           {
  69.             if(t>99949999)  //100秒以上显示Hxxx秒(99.5-999.49秒)(99950000-999489535)
  70.               {
  71.                 t += 500000;                          //十万位四舍五入
  72.                 Ledbuff[0] = LEDchar[t/1000000%10];   //个位显示
  73.                 Ledbuff[1] = LEDchar[t/10000000%10];  //十位显示
  74.                 Ledbuff[2] = LEDchar[t/100000000%10]; //百位显示
  75.                 Ledbuff[3] = 0xD3;                    //千位显示H
  76.               }
  77.           else if(t>9994999)  //100秒以下显示Hxx.x秒(9995000-99949999)
  78.             {
  79.               t += 50000;                           //万位小数四舍五入
  80.               Ledbuff[0] = LEDchar[t/100000%10];    //个位显示
  81.               Ledbuff[1] = LED_DDP[t/1000000%10];   //十位显示+.
  82.               Ledbuff[2] = LEDchar[t/10000000%10];  //百位显示
  83.               Ledbuff[3] = 0xD3;                     //千位显示H
  84.             }
  85.           else if(t>999949)  //1000ms及以上显示xxxx ms,无小数(999950-9999499)
  86.             {
  87.               t += 500;                             //百位小数四舍五入
  88.               Ledbuff[0] = LEDchar[t/1000%10];      //个位显示
  89.               Ledbuff[1] = LEDchar[t/10000%10];     //十位显示
  90.               Ledbuff[2] = LEDchar[t/100000%10];    //百位显示
  91.               Ledbuff[3] = LEDchar[t/1000000];      //千位显示
  92.             }
  93.           else if(t>99994)  //100ms及以上显示xxx.x ms,1位小数(99995-999949)
  94.             {
  95.               t += 50;                              //十位小数四舍五入
  96.               Ledbuff[0] = LEDchar[t/100%10];       //个位显示
  97.               Ledbuff[1] = LED_DDP[t/1000%10];      //十位显示+.
  98.               Ledbuff[2] = LEDchar[t/10000%10];     //百位显示
  99.               Ledbuff[3] = LEDchar[t/100000];       //千位显示
  100.             }
  101.           else if(t>9999) //10ms及以上显示xx.xx ms,2位小数(1000-99994)
  102.             {
  103.               t += 5;                              //个位小数四舍五入
  104.               Ledbuff[0] = LEDchar[t/10%10];        //个位显示
  105.               Ledbuff[1] = LEDchar[t/100%10];       //个位显示
  106.               Ledbuff[2] = LED_DDP[t/1000%10];      //十位显示+.
  107.               Ledbuff[3] = LEDchar[t/10000];        //百位显示
  108.             }
  109.           else   //10ms以下显示x.xxx ms,3位小数(0-9999)
  110.             {
  111.               Ledbuff[0] = LEDchar[t%10];          //个位显示
  112.               Ledbuff[1] = LEDchar[t/10%10];       //个位显示
  113.               Ledbuff[2] = LEDchar[t/100%10];      //十位显示
  114.               Ledbuff[3] = LED_DDP[t/1000];        //百位显示+.
  115.             }
  116.         }
  117.     }
  118. }

  119. /********************数码管显示*************************/
  120. void xianshi()
  121. {
  122.   static u8 i = 0;                              //静态变量i,显示使用

  123.   if(xianflag)
  124.     {
  125.       xianflag = 0;
  126.       P54 = 1;                //数码管消隐
  127.       P3 |= 0xE0;             //数码管P35.P36.P37消隐1110 0000=0xE0

  128.       switch(i)
  129.         {
  130.           case 0: P37 = 0;i++;P1 = Ledbuff[0]; break;    //个位
  131.           case 1: P54 = 0;i++;P1 = Ledbuff[1]; break;    //十位
  132.           case 2: P35 = 0;i++;P1 = Ledbuff[2]; break;    //百位
  133.           case 3: P36 = 0;i=0;P1 = Ledbuff[3]; break;    //千位
  134.           default: break;
  135.         }
  136.     }
  137. }

  138. /********************主函数*************************/
  139. void main()
  140. {
  141.   P1M0 = 0xFF; P1M1 = 0x00;    //推挽
  142.   P3M0 = 0x08; P3M1 = 0x00;    //P33推挽,其余为准双向口
  143.   P5M0 = 0x00; P5M1 = 0x00;    //双向口

  144.   PX0 = 1;                    //设置INT0中断为高级3 PX0=1
  145.   IPH |= 0x01;                                    //PXOH=1
  146.   PT0 = 0;                    //设置定时器0中断为高级2 PT0=0
  147.   IPH |= 0x02;                //0x02=00000010        PT0H=1

  148.   /********************定时器0设置*******************************/
  149.   AUXR &= 0x7B;              //01111011=0x7B,定时器0和定时器2为12T模式
  150.   TMOD = 0x09;               //GATE=1计数模式 0000 1001,定时器0位16位不自动重载
  151.   TL0 = 0x00;
  152.   TH0 = 0x00;
  153.   TR0 = 1;                  //启动定时器0

  154.   /**************定时器2设置*默认16位自动重载,中断级别0*****************/
  155.   T2H = 0xFC;               //Tim2定时器初始时值0xFC18=64536
  156.   T2L = 0x18;
  157.   AUXR |= 0x10;             //00010000=0x10,定时器2开始计时

  158.   ET0 = 1;                  //开启定时器0中断
  159.   IE2 |= 0x04;              //开启定时器2中断        0x04=0000 0100,ET2=0
  160.   IT0 = 1;                  //设置INT0为下降沿中断
  161.   EX0 = 1;                  //开启int0中断
  162.   EA = 1;                   //开启总中断

  163.   while (1)
  164.     {
  165.       keycan();
  166.       LED();
  167.       xianshi();
  168.     }
  169. }

  170. /************************INT0外部中断**********************************/
  171. void INT0_Isr() interrupt 0  //INT0中断,级别最高3
  172. {
  173.   ttH = TH0;
  174.   ttL = TL0;
  175.   ttyi = yi;
  176.   TL0 = 0x00;
  177.   TH0 = 0x00;
  178.   yi = 0;
  179.   gengxin = 1;
  180. }

  181. /********************计时器0中断*************************/
  182. void Time0() interrupt 1  //定时器0中断,级别高2
  183. {
  184.   yi++;
  185.   if(yi == 15250)        //超出测量范围,此时t最大值=999424000+65535=999.489535秒
  186.     {Err = 1;yi = 0;}
  187. }

  188. /**************定时器Tim2中断执行函数,产生10ms标志,级别低0****************/
  189. void Time2() interrupt 12
  190. {
  191.   static u8 i = 0;        //设置静态变量j,10ms标志用

  192.   xianflag = 1;
  193.   i++;
  194.   if(i == 10)
  195.     {
  196.       i = 0;
  197.       flag10ms = 1;       //产生10ms标志位,用于按键扫描
  198.     }
  199. }
  200. /****Program Size: data=26.4 xdata=0 code=1934******/
复制代码




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x

打赏

参与人数 5家元 +270 收起 理由
weln2016 + 30 原創內容
IlovePLC + 30 原創內容
兔包公 + 30 以資鼓勵
家睦 + 150
飞向狙沙 + 30 謝謝分享

查看全部打赏

发表于 2025-8-4 11:35:31 | 显示全部楼层
不加充电和开关机电路的话焊接工作量减半,我都打算以后尽量用掉电模式,砍掉开关机电路了,一堆元件焊的心烦
回复 支持 反对

使用道具 举报

发表于 2025-8-4 11:41:23 | 显示全部楼层
楼上两位大佬的作品都是精品!!!
回复 支持 反对

使用道具 举报

发表于 2025-8-4 18:27:10 来自手机浏览器 | 显示全部楼层
使用外部晶振的话,一般来说精度差不了多少。对于要求高的时钟测量建议还是使用外部晶振,大婶说的
回复 支持 反对

使用道具 举报

发表于 2025-8-4 20:51:02 | 显示全部楼层
运用范围比较广
回复 支持 反对

使用道具 举报

发表于 2025-8-5 14:12:32 | 显示全部楼层
想学习脉冲测量的思路,一直找不到相关案例,谢谢楼主分享,赞一个
回复 支持 反对

使用道具 举报

发表于 2025-8-5 20:53:27 | 显示全部楼层
谢谢分享好资料,先记号收下。
回复 支持 反对

使用道具 举报

发表于 2025-8-5 22:10:31 | 显示全部楼层
竟然是stc8的
回复 支持 反对

使用道具 举报

发表于 2025-8-6 09:13:43 | 显示全部楼层
谢谢分享~学习了
回复 支持 反对

使用道具 举报

发表于 2025-8-6 17:06:45 | 显示全部楼层
无原理图画PCB的是高手
回复 支持 反对

使用道具 举报

发表于 2025-8-6 21:28:27 | 显示全部楼层
谢谢分享,收藏备用
回复 支持 反对

使用道具 举报

发表于 2025-8-8 08:44:48 | 显示全部楼层
stc的单片机居然生命力还挺强的
回复 支持 反对

使用道具 举报

发表于 2025-8-8 20:22:27 来自手机浏览器 | 显示全部楼层
好高级,可惜我不会玩,买过大佬的点焊机控制板
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册 微信登录

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-8-18 08:03 , Processed in 0.234000 second(s), 11 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

快速回复 返回顶部 返回列表