本文将讲解51单片机在线缆摇摆测试机中的应用,包含单片机程序及人机界面程序设计,将以本人以前做过的一个案子为例来做讲解。
公司外购一款线缆,进料检验时,需要做摇摆测试,以判定品质是否合格。公司当时没有摇摆测试机,也曾外让采购外购,但因价格比较高,体积比较大,且不太适合该线缆的测试而搁浅,最后决定自制。
如果你以前没接触过线缆制造,也许对摇摆测试不是很了解,这里我就做些简单的介绍。我们常用的软线缆,如USB、HDMI、VGA线缆等。在使用过程中会受到弯曲,多次弯曲后可会能发生:外皮开裂,芯线断裂,连接器脱落、SR脱落、脱焊等问题。为将避免这些问题,需要在产品推向市场前做测试,看是否会发生这些问题,以便做针对性改善。通常的测试方法是:将线缆一端夹在一个轴为水平,可以正反旋转的转盘上;另一端悬挂指定质量的砝码;转盘在电机的带动下,按一定的速度与角度,反复正反转动规定的次数。由于测试时看起来是在摇摆,所以称它为摇摆测试,这个测试机也就是摇摆测试机。英文说法是“Cable flex test”,如HDMI标准中列出了“Cable flex”测试项,并指定了测试方法,如下:
设计方案确定 在明白了摇摆测试后,讲一下摇摆测试机的设计方案。这个摇摆测试机,搞得很简单。主要执行机构就是一个带减速器的57步进电机与固定在减速器输出轴上的样品装夹转盘。电机安装在一个铝型材的机架上。如下图:
电控部分:供电电源用的是24V/10A 的开关电源,步进驱动器是MC542G,控制采用STC IAP5W4K61S4单片机为主控的控制板,人机界面用的是7吋触摸(TJC8048K070_011R)。开关电源,步进驱动器,主板,及触摸屏一并装入一个仪表箱中,仪表箱由商家按图开孔。做好后的控制仪表箱如下图示:
人机界面设计 这个人机界面比较简单就两个页面,一个是主界面,如下:
另一个是输入键盘,如下:
主界面上控件只有两种,一种是文本控件与按钮控件。点击文本控件,即可弹出输入键盘页面实现输入设置,主页面背景使用色是PS做的图片。图片另存为一张图片在按钮部位做成不同的颜色,工作按钮按下切图。如下:
在主界面添加了三个变量,如下:
前两个为数值型,最后一个为字符串。下面看一下t0~t4按下事件代码,t0按下事件代码如下:
mstrlen.val=3 kflag.val=0 printh A0 page keybdB0
t1按下事件代码如下:
mstrlen.val=3 kflag.val=1 printh A0 page keybdB0
t2按下事件代码如下:
mstrlen.val=3 kflag.val=2 printh A0 page keybdB0
t3按下事件代码如下:
mstrlen.val=4 kflag.val=3 printh A0 page keybdB0
t4按下事件代码如下:
mstrlen.val=4 kflag.val=3 printh A0 page keybdB0
再看一下 主界面的按钮b0~b3,按下事件代码,b0按下事件代码如下:
mparameter.txt=t0.txt mparameter.txt=mparameter.txt+t1.txt+t2.txt+t3.txt+t4.txt printh A1
b1按下事件代码如下:
printh A2
b2按下事件代码如下:
printh A3
b3按下事件代码如下:
printh A4
再看一下键盘页面,键盘页面也是由两种控件组成,分别为文本控件与按钮控件,文本控件有两个
t0与show,t0用做背景,show用作输入显示。先看一下数字按牛的弹起事件代码(按下事件代码为空,没有按下事件代码),b1的弹起事件代码如下:
printh A0 strlen show.txt,sys0 if(sys0<page0.mstrlen.val) { show.txt=show.txt+"1" }
b2的弹起事件代码如下:
printh A0 strlen show.txt,sys0 if(sys0<page0.mstrlen.val) { show.txt=show.txt+"2" }
其他数字键的按钮的弹起事件代码,类似不再一一列举。
b10按钮(-)的作用是Back键的作用,弹起事件代码如下:
printh A0 show.txt=show.txt-1
b200按钮(DEL)的弹起事件代码如下:
printh A0 show.txt=""
b210按钮(OK)的弹起事件代码如下:
printh A0 if(page0.kflag.val==0) { strlen show.txt,sys0 if(sys0<page0.mstrlen.val) { page0.t0.txt="000" page0.t0.txt=page0.t0.txt-sys0 page0.t0.txt=page0.t0.txt+show.txt }else { page0.t0.txt=show.txt } }else if(page0.kflag.val==1) { strlen show.txt,sys0 if(sys0<page0.mstrlen.val) { page0.t1.txt="000" page0.t1.txt=page0.t1.txt-sys0 page0.t1.txt=page0.t1.txt+show.txt }else { strlen show.txt,sys0 if(sys0<page0.mstrlen.val) { page0.t1.txt="000" page0.t1.txt=page0.t1.txt-sys0 page0.t1.txt=page0.t1.txt+show.txt }else { page0.t1.txt=show.txt } } }else if(page0.kflag.val==2) { strlen show.txt,sys0 if(sys0<page0.mstrlen.val) { page0.t2.txt="000" page0.t2.txt=page0.t2.txt-sys0 page0.t2.txt=page0.t2.txt+show.txt }else { page0.t2.txt=show.txt } }else if(page0.kflag.val==3) { strlen show.txt,sys0 if(sys0<page0.mstrlen.val) { page0.t3.txt="0000" page0.t3.txt=page0.t3.txt-sys0 page0.t3.txt=page0.t3.txt+show.txt }else { page0.t3.txt=show.txt } }else if(page0.kflag.val==4) { strlen show.txt,sys0 if(sys0<page0.mstrlen.val) { page0.t4.txt="0000" page0.t4.txt=page0.t4.txt-sys0 page0.t4.txt=page0.t4.txt+show.txt }else { page0.t4.txt=show.txt } } page page0
以上代码中 带printh 行才是与当片机通信的代码行。printh A0是向单片机发送0xA0(让单片机控制蜂鸣器,发出按键音),输入键盘上,除了printh A0,没有其他带printh的代码行。主界面上,按下确定按钮(b0),打包设置参数到mparameter.txt,并发送0xA1(更新设置数据指令);按下开始测试按钮(b1),发送0xA2(开始测试指令);按下暂停测试按钮(b2),发送0xA3(暂停测试指令);按下测试停止按钮(b3),发送0xA4(停止测试指令)。
单片机程序设计 单片机通过PWM模块发送占空比为50%的PWM脉冲给步进驱动器做步进脉冲,单片机Timer 0 做计数器,对脉冲进行计数,以控制反复摆动角度及换向时机。通过外部中断侦测可能出现线缆芯线断裂及电阻异常。单片机与触控屏通过Uart1 实现串口通信。
端口定义 代码如下:
#ifndef __MYPORT_H__ #define __MYPORT_H__ #include "stc15w4k.h" #include "stc15w4kgpio.h" #define MONITOR_PORT P2 sbit BUZZER = P4^7; //buzzer control bit sbit INT0_IN = P3^2; //INT0 input pin, sample wire broken detect sbit puslePin = P4^4; sbit motorEnable = P4^3; sbit motorDirection = P4^2; sbit T0_In = P3^4; //T0 input pin for pusle counting sbit RxD_2 = P3^6; //Usart1 RxD sbit TxD_2 = P3^7; //Usart1 TxD #endif
主程序头文件 main.h代码如下:
#ifndef __MAIN_H__ #define __MAIN_H__ #include "myport.h" #include "stcpwm.h" #include "stc15w4kgpio.h" #include "stctimer.h" #include "stcuart.h" #include "delay.h" Timer_TypeDef mTimerStruct; //extern ui32 const FOSC; ui32 const FOSC = 30000000L; ui16 code Vbg_ROM _at_ 0xf3f7; //IAP15W4K61S4 volatile ui8 receiveDataFlag = 0; volatile ui8 mRdata = 0; volatile ui8 reFlag = 0; ui32 const motorsetp = 12800; //64*200 ui16 PAngle = 90; volatile ui16 nAngle = 90; volatile ui16 PAngle = 90; volatile ui32 pPusle = 2000; volatile ui32 nPusle = 2000; volatile ui8 mstr[18] = ""; volatile ui8 mstr1[5] =""; volatile ui8 mstr2[10] =""; volatile ui8 mNbits = 0; volatile ui16 speed = 60; volatile ui32 pusleFr = 1600; volatile ui32 targetConts = 500; volatile ui16 rCounts = 0; volatile ui8 runFalg = 0; volatile ui8 cycleFlag = 1; volatile ui8 finishFlag = 0; volatile ui8 suspendFlag = 0; volatile ui8 initData = 0; ui8 const GRATION = 1; ui8 const CALRaTIO = 5; //UART1_TYPDEF mUART1_Struct; //************************************************** void SoundBuzzer(); //******************************************************* void SendQuMark(); //send half double quotation mark //*************************** void SendEndMark(); //Send end mark #endif
主程序 main.c 代码如下:
//***************************************************** void Usart1_Routine(void) interrupt 4 { if(!receiveDataFlag) { if(RI) { RI=0; mRdata = SBUF; reFlag = 1; } } } //End of Usart1_Routine //***************************************************** void Timer0Int() interrupt 1 { ++cycleFlag; PWMCR &= 0x7F; //PWM disable switch(cycleFlag) { case 1: rCounts++; TR0 = 0; TL0 = 65536 - pPusle; TH0 = (65536 - pPusle) >> 8; TR0 = 1; break; case 2: motorDirection = !motorDirection; TR0 = 0; TL0 = 65536 - pPusle; TH0 = (65536 - pPusle) >> 8; TR0 = 1; break; case 3: TR0 = 0; TL0 = 65536 - nPusle; TH0 = (65536 - nPusle) >> 8; TR0 = 1; break; case 4: motorDirection = !motorDirection; cycleFlag = 0; TR0 = 0; TL0 = 65536 - nPusle; TH0 = (65536 - nPusle) >> 8; TR0 = 1; break; } if(rCounts >= targetConts) { //runFalg = 0; finishFlag = 1; TR0 = 0; } else PWMCR |= 0x80; //PWM enable } //End of Timer0_Routine() interrupt 1 //***************************************************** void Timer1Int() interrupt 3 { PWMCR &= 0x7F; //PWM disable TR1 = 0; SoundBuzzer(); } void main() { GPIOInit(0xFF, 0xFF,BI_IO); P0 =0xFF; P1 =0xFF; P2 =0xFF; P3 =0xFF; P4 =0xFF; P5 =0xFF; P6 =0xFF; P7 =0xFF; //************************************** EA = 1; GPIOInit(GPIO_P4, GPIO_PIN4, PP_OUT); //init P4.4 PP_OUT P_SW2 |= 1 << 7; //visit extend SRF enable Usart1Mode(0x01); //8bits variable baud rate Usart1MulComDisable(); //multip machine communitate disable Usart1ReceiveEnable(); UsartBaudRateDouble(0); //without double baud rate Usart1BaudTimer(1);//selsct baud rate timer2 Usart1BaudRateFreDivDis(); Usart1Pin(0x01);//P3.6/RxD_2, P3.7/TxD_2 GPIOInit(GPIO_P3, GPIO_PIN7, PP_OUT); //push_pull out Usart1IntEnable();//Usart1 interrupt enable Usart1BaudRate(9600);//configure Usart1 baud rate 9600 PS = 1; Usart1ResetTi();//set TI to 0 Usart1ResetRi();//set TI to 0 TIMER_T2Start();//Timer2 start Timer_Initstruct(&mTimerStruct); mTimerStruct.nTimer = timer0; mTimerStruct.mWorkMode = _load16bit; mTimerStruct.CountEnable = 1; //work as counter mTimerStruct.IntEnable = 1; //int enable Timer_Init(&mTimerStruct); SoundBuzzer(); Delay100xms(5,8); SoundBuzzer(); Delay100xms(5,8); SoundBuzzer(); Delay100xms(5,8); motorEnable = 1; while(1) { if(reFlag) { reFlag = 0; switch(mRdata) { case 0xA0: SoundBuzzer(); break; case 0xA1: //parameter configure ok if(!runFalg) { SoundBuzzer(); //Delay10xms(2,8); memset(mstr,0, strlen(mstr)); receiveDataFlag = 1; mNbits = 0; UART1_SendString("prints page0.mparameter.txt,0"); SendEndMark(); RI = 0; while(mNbits < 17) { while(!RI); RI = 0; mstr[mNbits] = SBUF; mNbits++; } receiveDataFlag = 0; mNbits = 0; UART1_SendString("page0.t7.txt="); SendQuMark(); UART1_SendString(mstr); SendQuMark(); SendEndMark(); Delay100xms(10,8); //memset(mstr1, 0, strlen(mstr1)); StringSub(mstr1, mstr, 0, 3); PAngle = atol(mstr1); UART1_SendString("page0.t0.txt="); SendQuMark(); UART1_SendString(mstr1); SendQuMark(); SendEndMark(); //memset(mstr1, 0, strlen(mstr1)); StringSub(mstr1, mstr, 3, 3); nAngle = atol(mstr1); UART1_SendString("page0.t1.txt="); SendQuMark(); UART1_SendString(mstr1); SendQuMark(); SendEndMark(); //memset(mstr1, 0, strlen(mstr1)); StringSub(mstr1, mstr, 6, 3); speed = atol(mstr1); UART1_SendString("page0.t2.txt="); SendQuMark(); UART1_SendString(mstr1); SendQuMark(); SendEndMark(); //memset(mstr1, 0, strlen(mstr1)); StringSub(mstr1, mstr, 14, 4); targetConts = atol(mstr1); UART1_SendString("page0.t4.txt="); SendQuMark(); UART1_SendString(mstr1); SendQuMark(); SendEndMark(); pusleFr = (f32)motorsetp * speed /10800 * GRATION * (PAngle + nAngle); //360*60/2=10800 pPusle = (f32)motorsetp * PAngle * GRATION * CALRaTIO/ 720; nPusle = (f32)motorsetp * nAngle * GRATION * CALRaTIO/ 720; LongtoString(pPusle,mstr2); //nAngle UART1_SendString("page0.t7.txt="); SendQuMark(); UART1_SendString(mstr2); SendQuMark(); SendEndMark(); Delay100xms(10,8); LongtoString(nPusle,mstr2); //nAngle UART1_SendString("page0.t7.txt="); SendQuMark(); UART1_SendString(mstr2); SendQuMark(); SendEndMark(); Delay100xms(10,8); UART1_SendString("page0.t7.txt=\"\""); SendEndMark(); } break; case 0xA2: //start test SoundBuzzer(); if(!runFalg) { runFalg = 1; initData = MONITOR_PORT; motorDirection = 1; //init timer0 TF0 = 0; TR0 = 0; TL0 = 65536 - pPusle; TH0 = (65536 - pPusle) >> 8; TIMER_T0Start(); motorEnable = 1; PWM_Out(PWM_CH4, pusleFr, 0, O2); } else { if(suspendFlag) { PWMCR |= 0x80; //PWM enable suspendFlag = 0; } if(finishFlag == 1) runFalg = 0; } break; case 0xA3: //suspend test SoundBuzzer(); PWMCR &= 0x7F; //PWM disable suspendFlag = 1; break; case 0xA4: //stop test SoundBuzzer(); PWMCR &= 0x7F; //PWM disable TR0 = 0; suspendFlag = 0; finishFlag = 0; runFalg = 0; rCounts = 0; cycleFlag = 1; LongtoString(rCounts,mstr2); //nAngle UART1_SendString("page0.t6.txt="); SendQuMark(); UART1_SendString(mstr2); SendQuMark(); SendEndMark(); break; } mRdata = 0; } if(runFalg) { if(initData <= MONITOR_PORT) { UART1_SendString("page0.t5.txt="); SendQuMark(); UART1_SendString("测试中"); SendQuMark(); SendEndMark(); LongtoString(rCounts,mstr2); //nAngle UART1_SendString("page0.t6.txt="); SendQuMark(); UART1_SendString(mstr2); SendQuMark(); SendEndMark(); } else //Test Fail { PWMCR &= 0x7F; //PWM disable runFalg = 0; finishFlag = 2; } if(suspendFlag) { UART1_SendString("page0.t5.txt="); SendQuMark(); UART1_SendString("暂停中"); SendQuMark(); SendEndMark(); } } else { switch(finishFlag) { case 0: UART1_SendString("page0.t5.txt="); SendQuMark(); UART1_SendString("空闲"); SendQuMark(); SendEndMark(); break; case 1: UART1_SendString("page0.t5.txt="); SendQuMark(); UART1_SendString("测试完成,Pass!"); SendQuMark(); SendEndMark(); break; case 2: UART1_SendString("page0.t5.txt="); SendQuMark(); UART1_SendString("测试失败,Fail!"); SendQuMark(); SendEndMark(); UART1_SendString("page0.t5.bco=63488"); SendEndMark(); break; } } } } //****************************************************/ void SoundBuzzer() { BUZZER = 0; Delay100xms(3,8); BUZZER = 1; } //End of SoundBuzzer() //******************************************************* void SendQuMark() //send half double quotation mark { ES = 0; Usart1SendByte(34); ES = 1; } //End of Usart1BaudRate(ui32 myBaudRate) //*************************** void SendEndMark() //Send end mark { ES = 0; Usart1SendByte(0xFF); Usart1SendByte(0xFF); Usart1SendByte(0xFF); ES = 1; } //End of SendEndMark()
上面代码中省去了,测试中线缆异常的处理代码。机器的运行效果如下图: