本帖最后由 刘佑红 于 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文件。
- /************************测量脉冲宽度******************************
- STC8H1K08 TSSOP20 IRC内部时钟选12MHZ
- P37 个位 数码管位驱动0
- P54 十位 数码管位驱动1
- P34 百位 数码管位驱动2
- P35 千位 数码管位驱动3
- P32 into 脉冲输入端
- 硬件版本:适用20250613(无开关机即负脉冲输入功能)
- 软件版本:第一版12T12MHZ
- *****************************************************************/
- #include<stc8h.h> //STC8H1K08 TSSOP20
- #include <intrins.h> //调用_nop_函数
- typedef unsigned char u8; //对数据类型进行定义声明
- typedef unsigned int u16; //对数据类型进行定义声明
- typedef unsigned long u32;//对数据类型进行定义声明
- sbit Kset = P3^4; //SET按键
- bit flag10ms = 0; //10ms中断标志
- bit Err = 0; //脉宽超时标志
- bit gengxin = 1; //更新标志
- bit xianflag = 0; //显示标志
- u8 ttH = 0; //计数高8位
- u8 ttL = 0; //计数低8位
- u16 ttyi = 0; //定时器0计数溢出J计数
- u16 yi = 0; //定时器0计数溢出次数
- u32 t = 0; //脉宽计数
- /**********************数码管真值表-共阴0-9***************/
- u8 code LEDchar[10]={0xF9,0x81,0xBA,0xAB,0xC3,0x6B,0x7B,0xA1,0xFB,0xEB};
- u8 code LED_DDP[10]={0xFD,0x85,0xBE,0xAF,0xC7,0x6F,0x7F,0xA5,0xFF,0xEF};//加小数点
- u8 Ledbuff[4]={0xF9,0xF9,0xF9,0xF9}; //H=0xD3,E=0x7A,r=0x12
- /********************按键扫描*************************/
- void keycan()
- {
- static u8 set = 0; //按键扫描次数
- if(flag10ms) //10ms时间标志,由Tim2中断10次计数产生
- {
- flag10ms = 0; //清零10ms标志
- if(Kset) {set = 0;} //按键按下过程中抖动或者弹起
- else //按键按下
- {
- set++; //10ms扫描一次加1
- if(set == 2)
- {
- Err = 0;
- ttH = 0;
- ttL = 0;
- ttyi = 0;
- gengxin = 1;
- }
- }
- }
- }
- /********************更新时间显示*************************/
- void LED()
- {
- if(gengxin)
- {
- gengxin = 0;
- t = ttH*256L + ttL + ttyi*65536L;//脉宽计数,时间为us
- t = t*513/512; //误差校正
- if(Err) //超量程显示错误标志Err
- {
- Ledbuff[0] = 0x00; //个位不显示
- Ledbuff[1] = 0x12; //十位显示r
- Ledbuff[2] = 0x12; //百位显示r
- Ledbuff[3] = 0x7A; //千位显示E
- }
- else //正常量程
- {
- if(t>99949999) //100秒以上显示Hxxx秒(99.5-999.49秒)(99950000-999489535)
- {
- t += 500000; //十万位四舍五入
- Ledbuff[0] = LEDchar[t/1000000%10]; //个位显示
- Ledbuff[1] = LEDchar[t/10000000%10]; //十位显示
- Ledbuff[2] = LEDchar[t/100000000%10]; //百位显示
- Ledbuff[3] = 0xD3; //千位显示H
- }
- else if(t>9994999) //100秒以下显示Hxx.x秒(9995000-99949999)
- {
- t += 50000; //万位小数四舍五入
- Ledbuff[0] = LEDchar[t/100000%10]; //个位显示
- Ledbuff[1] = LED_DDP[t/1000000%10]; //十位显示+.
- Ledbuff[2] = LEDchar[t/10000000%10]; //百位显示
- Ledbuff[3] = 0xD3; //千位显示H
- }
- else if(t>999949) //1000ms及以上显示xxxx ms,无小数(999950-9999499)
- {
- t += 500; //百位小数四舍五入
- Ledbuff[0] = LEDchar[t/1000%10]; //个位显示
- Ledbuff[1] = LEDchar[t/10000%10]; //十位显示
- Ledbuff[2] = LEDchar[t/100000%10]; //百位显示
- Ledbuff[3] = LEDchar[t/1000000]; //千位显示
- }
- else if(t>99994) //100ms及以上显示xxx.x ms,1位小数(99995-999949)
- {
- t += 50; //十位小数四舍五入
- Ledbuff[0] = LEDchar[t/100%10]; //个位显示
- Ledbuff[1] = LED_DDP[t/1000%10]; //十位显示+.
- Ledbuff[2] = LEDchar[t/10000%10]; //百位显示
- Ledbuff[3] = LEDchar[t/100000]; //千位显示
- }
- else if(t>9999) //10ms及以上显示xx.xx ms,2位小数(1000-99994)
- {
- t += 5; //个位小数四舍五入
- Ledbuff[0] = LEDchar[t/10%10]; //个位显示
- Ledbuff[1] = LEDchar[t/100%10]; //个位显示
- Ledbuff[2] = LED_DDP[t/1000%10]; //十位显示+.
- Ledbuff[3] = LEDchar[t/10000]; //百位显示
- }
- else //10ms以下显示x.xxx ms,3位小数(0-9999)
- {
- Ledbuff[0] = LEDchar[t%10]; //个位显示
- Ledbuff[1] = LEDchar[t/10%10]; //个位显示
- Ledbuff[2] = LEDchar[t/100%10]; //十位显示
- Ledbuff[3] = LED_DDP[t/1000]; //百位显示+.
- }
- }
- }
- }
- /********************数码管显示*************************/
- void xianshi()
- {
- static u8 i = 0; //静态变量i,显示使用
- if(xianflag)
- {
- xianflag = 0;
- P54 = 1; //数码管消隐
- P3 |= 0xE0; //数码管P35.P36.P37消隐1110 0000=0xE0
- switch(i)
- {
- case 0: P37 = 0;i++;P1 = Ledbuff[0]; break; //个位
- case 1: P54 = 0;i++;P1 = Ledbuff[1]; break; //十位
- case 2: P35 = 0;i++;P1 = Ledbuff[2]; break; //百位
- case 3: P36 = 0;i=0;P1 = Ledbuff[3]; break; //千位
- default: break;
- }
- }
- }
- /********************主函数*************************/
- void main()
- {
- P1M0 = 0xFF; P1M1 = 0x00; //推挽
- P3M0 = 0x08; P3M1 = 0x00; //P33推挽,其余为准双向口
- P5M0 = 0x00; P5M1 = 0x00; //双向口
- PX0 = 1; //设置INT0中断为高级3 PX0=1
- IPH |= 0x01; //PXOH=1
- PT0 = 0; //设置定时器0中断为高级2 PT0=0
- IPH |= 0x02; //0x02=00000010 PT0H=1
- /********************定时器0设置*******************************/
- AUXR &= 0x7B; //01111011=0x7B,定时器0和定时器2为12T模式
- TMOD = 0x09; //GATE=1计数模式 0000 1001,定时器0位16位不自动重载
- TL0 = 0x00;
- TH0 = 0x00;
- TR0 = 1; //启动定时器0
- /**************定时器2设置*默认16位自动重载,中断级别0*****************/
- T2H = 0xFC; //Tim2定时器初始时值0xFC18=64536
- T2L = 0x18;
- AUXR |= 0x10; //00010000=0x10,定时器2开始计时
- ET0 = 1; //开启定时器0中断
- IE2 |= 0x04; //开启定时器2中断 0x04=0000 0100,ET2=0
- IT0 = 1; //设置INT0为下降沿中断
- EX0 = 1; //开启int0中断
- EA = 1; //开启总中断
- while (1)
- {
- keycan();
- LED();
- xianshi();
- }
- }
- /************************INT0外部中断**********************************/
- void INT0_Isr() interrupt 0 //INT0中断,级别最高3
- {
- ttH = TH0;
- ttL = TL0;
- ttyi = yi;
- TL0 = 0x00;
- TH0 = 0x00;
- yi = 0;
- gengxin = 1;
- }
- /********************计时器0中断*************************/
- void Time0() interrupt 1 //定时器0中断,级别高2
- {
- yi++;
- if(yi == 15250) //超出测量范围,此时t最大值=999424000+65535=999.489535秒
- {Err = 1;yi = 0;}
- }
- /**************定时器Tim2中断执行函数,产生10ms标志,级别低0****************/
- void Time2() interrupt 12
- {
- static u8 i = 0; //设置静态变量j,10ms标志用
- xianflag = 1;
- i++;
- if(i == 10)
- {
- i = 0;
- flag10ms = 1; //产生10ms标志位,用于按键扫描
- }
- }
- /****Program Size: data=26.4 xdata=0 code=1934******/
复制代码
|