本文将讲解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()
上面代码中省去了,测试中线缆异常的处理代码。机器的运行效果如下图: