基于51单片机的称重系统
一 动态称重所谓动态称重是指通过分析和测量车胎运动中的力,来计算该运动车辆的总重量、轴重、轮重和部分重量数据的过程。动态称重系统按经过车辆行驶的速度划分,可分为低速动态称重系统与高速动态称重系统。因为我国高速公路的限速最高是120,所以高速动态称重系统在理论上可对5到120之间时速通过称量装置的车辆进行动态称重。而低速动态称重系统则一定要限制通过车辆的行驶速度,要想有较高的测量精度,理论要求车辆在5km/h以下时速匀速通过。在我国,车辆动态称重一般都使用低速动态称重来完成,在很多收费站和车辆检测站都有应用,国家也出台了相关的测量标准。
与传统意义上的静态称重相比,动态称重可以在车辆缓慢运动情况下直接进行称重,这样动态称重的高效率、测量时间短、能流畅交通等主要特点就凸显出来了。动态称重的问世,不但使车辆的管理上有了很大的促进作用,而且还对我国的公路管理和维护起到了至关重要的作用。
二 系统总体结构及其功能设计总体结构是以51单片机为处理器的系统,如图3.1所示。
图 3.1
本设计要求能判断出车辆是否超载,如果车辆超载,本系统能够提供该车辆的超载信息并发出警报。本设计采用STC89C52单片机作为系统的处理核心,利用桥式称重传感器采集到车辆重量并转换成电压信号,然后通过放大电路将电压信号进行放大处理后,传送到A/D转换器中转换为数字信号,再经过单片机处理、传输到接口电路,最后送到上位机,该数据可以与上位机里用键盘事先输入设定的总重量作比较并判断出该车辆是否超载,如果超载,则可通过显示器、蜂鸣器作显示超载信息并报警,当然,键盘的作用除了输入设定值还可以解除和开启警报。
三 动态称重系统的组成动态称重系统主要由车辆重量(含超载、偏载检测)检测子系统、货车长、宽、高三维尺寸超限检测子系统、自动触发摄像拍照子系统、车辆类型自动判别子系统、系统配置及系统维护子系统、行驶车辆速度测量子系统、数据统计、报表处理子系统和单据输出打印子系统这几部分组成。该系统组成完善,部件考虑周全,能很好的完成称重任务。
四 动态称重系统的主要功能(1) 动态检测出通过车辆的轴数、轴重、轴距、轮数、车速等;
(2) 能自动检测出车辆的高、宽、长等外围尺寸是否超出最大标准,并能给出超出部位的具体位置和具体数据;
(3) 拍摄机器在车辆经过时能自行对要被检测的车辆进行拍照,该机器能对车牌号码、车辆种类进行识别,最终作为图像证据;
(4) 可以将不合格车辆的处理记录、超限情况进行打印,根据车辆超限的程度来计算罚款数额并打印收据或罚款单;
(5) 检测到的数据全部存入数据库中,并对被监测到的数据进行分析、统计。便于汇总上报、日常管理和进行查询。
五 单片机的选用本设计采用的是INTEL公司研究开发生产的STC89C52单片机,其内部置有256字节的内部数据存储器、8位中央处理单元、8K片内程序存储器、3个16位定时/计数器、32个双向I/O口和一个片内时钟振荡电路,全双工串行通信口, 5个两级中断结构。89C52的引脚图如图4.1所示。
图 4.1 89C52引脚图
本设计使用的是单片机的最小系统,其中电路包括下载口电路、复位电路和晶振电路。复位电路中,电阻在下接地,电容在上接高电平,中间为RST。复位电路工作原理是:通电时,由于电流很大,从而相当于电容被短路,这样RST引脚上处于高电平,这时的单片机为禁止工作状态。如果要使单片机正常工作,就要使RST端电压慢慢下降并到一定程度,也就是RST端为低电平,这就需要通过电源对电容进行充电。
复位电路复位的方式有手动复位、上电复位两种。所谓上电复位,就是通电瞬间,由于电流很大,从而相当于电容被短路,这样RST引脚上处于高电平,自动复位;相反,通过对改变电容电流,当电流很小的时候,我们就可以把电容当做开路状态,RST端就处于低电平,程序就能正常的运行。而手动复位要在上电复位的基础上,按下复位按键,使VCC直接与RST相连,电容处于放电状态,以高电平形成复位;松开复位按键,RST仍旧是高电平,这时充电电流作用于电阻上,VCC给电容进行充电,还是复位状态,充电结束后,RST为低电平,能够正常工作。
A/D转换器A/D转换器根据输出的信号格式有并行A/D和串行A/D两种。ADC0832 是一种具有双通道 A/D 转换芯片和8 位分辨率。它的优点有体积小,兼容性强,性价比高,从而深受个人的欢迎和企业的认可,目前在世界上也已经有了较高的使用率。
ADC0832具有能够进行双通道A/D 转换,分辨率高达8位;当供电电源为5V时,输入电压能稳定的保持在0~5V 之间;TTL/CMOS与输出输入电平兼容;功耗很低,只有15mW;转换工作时间只有 32μS,也就是频率仅有 250KHZ等特点。其引脚功能图如图4.6所示,芯片引脚接口说明如表4所示。
图4.6 引脚排列
表4 ADC0832引脚说明
通常情况下的单片机和ADC0832的接口的数据线应为4条,分别是 CS、CLK、DO、DI。由于ADC0832的数据信号输入输出口与单片机具有双向接口通信,输入输出口也不同时使用,所以可以将数据信号输入输出口并联后当一条数据线进行使用。它们的硬件接口电路与单片机连接的原理如图4.7所示。
最后将以上的惠思登电桥、放大器、ADC0832转换器和STC89C52单片机连接起来,就组成了系统的采集模块。
图4.7 ADC0832 与单片机的接口电路
报警模块本系统要实现一旦检测到车辆超载超限,就会立刻鸣笛报警,通过操作人员的检查处理后,解除报警。本设计选用蜂鸣器作为发声装置,蜂鸣器可利用三极管来进行放大驱动。该接口电路如图4.15所示:
图4.15 报警接口电路
- 1 -
5. 系统的软件设计
5.1 主程序设计当系统上电复位后,系统开始初始化,包括端口等;初始化完毕后,调用串口输出提示语,开始准备串口输出电压;准备完毕后,调用串口输出电压值,开始从串口输出电压值;输出完毕后,调用串口输出换行值;最后开始延时200ms。根据系统方案,设计出本设计的主程序流程,可以用框图表示。
图5.1 主程序流程图
5.3 ADC0832软件设计首先要将芯片开始使能,即CS使能端置于低电平,然后通过DI和DO的同一数据输入端口,可实现通道功能的选择,再调用通道初始化程序,初始化完毕后,在8个时钟边沿获得正序和反序8位数据,最后返回数据。根据此方案,设计出本设计中A/D转换程序流程,如图5.2所示。
当两位数据都为0时,CH1作为负输入端 IN-,而CH0就作为正输入端IN+来进行相关输入。当此两位数据都为1时,CH1进行单通道转换而CHO不转换。当两位数据分别为0和1时,CH1作为正输入端IN+,CH0作为负输入端IN-来进行相关输入。当两位数据为分别为1和0时,CH0进行单通道转换而CH1不转换。ADC0832的功能项如表7所示。
图5.2 ADC0832转换流程图
表7 AD0832功能项
ADC0832没有工作时,DO/DI和CLK的电平可高可低,但CS的输入端口应必须显示高电平,此时芯片处于禁用状态。当A/D转换进行时,CS端口必须处于低电平并且一直将低电平保持到转换全部结束。当芯片转换工作开始,芯片的CLK端口会接收到处理器传送来的一时钟脉冲,DO/DI并联端口将使用数据输入信号的DI端口。
第一个时钟脉冲的下沉出现之前,DI端口一定要是高电平,表示启始信号的发出,在第二、三个脉冲的下沉出现之前,DI端口要输入两位数据来选择通道。第三个脉冲出现下沉之后,DI端口就不再起任何作用,此后 DO/DI并联端口则是被DO端口占领进行读取转换数据。第四个下沉脉冲出现开始,DO端口输出最高位的转换数据DATA7,接下来每个脉冲下沉之后DO端口都会输出下一位的转换数据。直到发出最低位数据DATA0,也就是由第十一个脉冲发出的数据之后,这样一个字节的数据输出就完成了。再从第十一个脉冲下沉开始从DATD0开始输出下一个相反数据字节。然后一直到第十九个脉冲完成数据的输出,这样一次A/D 转换才结束。最后,要想将转换后的数据进行相关处理就必须将芯片禁用,也就是将CS端口输入高电平。
5.4 LCD显示程序设计首先设置显示模式,设置第(x,y)个字符的DDRAM的地址,为15×2显示,因为液晶显示为15列,所以x位置的范围是0到15,同理,因为显示2行,所以y位置的范围是0到1。显示程序如下:
void Lcd_Pos(uchar yPos,uchar xPos)
{
uchar tmp;
xPos &= 0x0f; //x位置范围是0~15,因为显示15列
yPos &= 0x01; //y位置范围是0~1,因为显示2行
if(yPos==0) //显示第一行
{
tmp = xPos;
}
else
{
tmp = xPos + 0x40; //显示第二行
}
tmp |= 0x80;
Write_com(tmp);
}
5.5 主函数软件主要分成四个部分:串口配置,ADC0832的初始化,等待接受数据和输出数据。程序如下:
void main(void)
{
InitUART(); //串口初始化
Lcd_init();
Write_String("Weight: ", 0, 0);
Write_String("H=", 1, 0);
Write_String("L=", 1, 6);
Beep = 1;
while(1)
{
Process10ms();
DispVal(Wh, 1, 2);
DispVal(Wl, 1, 8);
CheckProcess();
if (flagget10s == 1)
{
flagget10s = 0;
Get_temp(sum*100);
ET0 = 1;
TR0 = 1;
Disp_Voltage(); //采集电压并发送
}
}
}
单片机系统部分硬件原理图单片机程序
代码:
- #include "reg52.h"
- #include "My_type.h" //数据类型头文件
- #include <intrins.h>
-
- #define nop() _nop_()
- #define uchar unsigned char
- #define uint unsigned int
-
- sbit Lcd_rs=P2^0;
- sbit Lcd_rw=P2^1;
- sbit Lcd_en=P2^2;
-
- sbit key1 = P3^5;
- sbit key2 = P3^6;
- sbit key3 = P3^7;
-
- sbit Beep = P2^7;
- sbit Led = P2^6;
- sbit CS=P1^3; //使能
- sbit CLK=P1^1; //时钟
- sbit Do=P1^2; // 数据输出
- sbit Di=P1^2; //数据输入
-
- #define first_channel 0x02 //通道1
- #define second_channel 0x03 //通道2
- uchar CH = first_channel;
-
- #define Fclk 11059200UL /*使用11.0592M晶体*/
- #define BitRate 9600UL /*波特率定义为9600*/
- uint8 Sending; //发送标志
-
- code uint16 AD_Tab[41] = {512, 2048, 2970, 3840, 4659, 5581,
- 6349, 7117, 7782, 8397, 9165, 9830,
- 10291, 11162, 11520, 11981, 12749, 13210,
- 13926, 14490, 15206, 15821, 16538, 17357,
- 17971, 18842, 19814, 20838, 21760, 22477,
- 23091, 23603, 23962, 24371, 24678, 24883,
- 25037, 25190, 25293, 25395, 25600};
-
- uint8 temp_zheng;
- float temp_xiao;
-
- uint8 flag10ms = 0;
- uint8 flag50ms = 0;
- uint8 get10s = 0;
- uint8 flagget10s = 0;
- uint8 Alarmflag = 1;
- uint16 sum = 0;
- uint8 count5ms = 0;
- uint8 Wh = 40;
- uint8 Wl = 20;
-
- void Delay_lcd1602(uint dly)
- {
- uint i;
- for(; dly>0; dly--)
- for(i=0; i<100; i++);
- }
-
- bit Lcd_busy()
- {
- bit result;
- Lcd_rw = 1;
- Lcd_rs = 0;
- Lcd_en = 1;
- nop();nop();nop();nop();
- result = (bit)(P0&0x80);
- Lcd_en = 0;
- return(result);
- }
-
- void Write_com(uchar com)
- {
- while(Lcd_busy()); //LCD忙等待
- Lcd_rs = 0;
- Lcd_rw = 0;
- P0 = com;
- Delay_lcd1602(5);
- Lcd_en = 1;
- Delay_lcd1602(5);
- Lcd_en = 0;
- }
- void Write_data(uchar date)
- {
- while(Lcd_busy()); //LCD忙等待
- Lcd_rs = 1;
- Lcd_rw = 0;
- P0 = date;
- Delay_lcd1602(5);
- Lcd_en = 1;
- Delay_lcd1602(5);
- Lcd_en = 0;
- }
- void Lcd_init()
- {
- Lcd_en = 0;
- Write_com(0x38);
- Delay_lcd1602(5);
- Write_com(0x0c);
- Delay_lcd1602(5);
- Write_com(0x04);
- Delay_lcd1602(5);
- Write_com(0x01);
- Delay_lcd1602(5);
- }
- void Lcd_Pos(uchar yPos,uchar xPos) //设置第(xPos,yPos)个字符的DDRAM地址
- {
- uchar tmp;
- xPos &= 0x0f; //x位置范围是0~15,因为显示15列
- yPos &= 0x01; //y位置范围是0~1,因为显示2行
- if(yPos==0) //显示第一行
- {
- tmp = xPos;
- }
- else
- {
- tmp = xPos + 0x40; //显示第二行
- }
- tmp |= 0x80;
- Write_com(tmp);
- }
-
- void Write_char(uchar c,uchar xPos,uchar yPos) //定义Write_Char函数
- {
- Lcd_Pos(xPos,yPos);
- Write_data(c);
- }
-
-
-
- void Write_String(uchar *s,uchar xPos,uchar yPos) //定义Write_String函数
- {
- uchar i = 0;
- Lcd_Pos(xPos,yPos);
- while(*s)
- {
- Write_data(*(s++));
- if(++i>16) break;
- }
- }
-
-
- void InitUART(void)
- {
- EA=0;
- TMOD|=0x21; //定时器1工作在模式2
- SCON=0x50; //串口工作在模式1
- TCON=0x05;
- TH1=256-Fclk/(BitRate*12*16);
- TL1=256-Fclk/(BitRate*12*16);
-
- TH0 = (65535 - 1000)/256;
- TL0 = (65535 - 1000)%256;
- ET0 = 1;
- TR0 = 1;
-
- PCON=0x80; //串口波特率加倍
- ES=1; //串行中断允许
- TR1=1; //启动定时器1
- REN=1; //允许接收
- EA=1; //允许中断
- }
- void UartISR(void) interrupt 4
- {
- if(RI) //收到数据
- {
- RI=0; //清中断请求
- }
- else //发送完一字节数据
- {
- TI=0;
- Sending=0; //清正在发送标志
- }
- }
- void PutChar_to_Uart(uint8 d)
- {
- Sending=1;
- SBUF=d;
- while(Sending);
- }
- void Prints(uint8 *pd)
- {
- while((*pd)!='\0')
- {
- PutChar_to_Uart(*pd);
- pd++;
- }
- }
-
-
- unsigned char ADconv(void)
- {
- unsigned char i;
- unsigned int data_f=0,data_c=0;
- ET0 = 0;
- TR0 = 0;
- Di=1;
- CS=1;
- _nop_();
- CS=0;
- Di=1; ;//芯片使能之前的初始化。第一个下降沿
- CLK=1;
- _nop_();
- _nop_();
-
- CLK=0; // 确定通道模式、第2个下降沿
- _nop_();
- _nop_();
- CLK=1;
- Di=(bit)(0x02&CH); //设定通道初始化
- _nop_();
- CLK=0;
- _nop_();
- _nop_();
- CLK=1;
- Di=(bit)(0x01&CH); //设定通道初始化 .第3个下降沿
- _nop_();
- _nop_();
- CLK=0; //AD转化的初始化完成。
- Di=1;
- CLK=1;
- _nop_();
- _nop_();
- CLK=0;
- _nop_();
- CLK=1;
-
-
- for(i=8;i>0;i--)//得到一个正常排序的8位数据
- {
- data_f|=Do;
- data_f<<=1;
- CLK=1;
- _nop_();
- _nop_();
- CLK=0;
- _nop_();
- }
- for(i=8;i>0;i--)//得到一个反序排列的8位数据
- {
- data_c<<=1;
- data_c|=Do;
- _nop_();
- CLK=1;
- _nop_();
- _nop_();
- CLK=0;
- _nop_();
- }
- CLK=0;
- _nop_();
- _nop_();
- CLK=1;
- _nop_();
- _nop_();
- CLK=0;
- _nop_();
- _nop_();
- CLK=1;
- _nop_();
- CS=1;
- _nop_();
- _nop_();
- ET0 = 1;
- TR0 = 1;
- return data_f;
- }
- /*
- void delay_ms(unsigned int x)
- {
- unsigned int i,j;
- i=0;
- for(i=0;i<x;i++)
- {
- j=108;
- while(j--);
- }
- }
-
-
-
-
-
- */
-
- void DispVal(uint8 pdat, uint8 x, uint8 y)
- {
- /*
- PutChar_to_Uart(pdat/100 + 0x30);
- PutChar_to_Uart(pdat%100/10 + 0x30);
- PutChar_to_Uart(pdat%100%10 + 0x30);
- */
- Write_char(pdat/100 + 0x30, x, y);
- Write_char(pdat%100/10 + 0x30, x, y+1);
- Write_char(pdat%100%10 + 0x30, x, y+2);
- }
-
-
- void Process10ms(void)
- {
-
- if (flag10ms == 1)
- {
- flag10ms = 0;
- count5ms ++;
- if (count5ms == 5)
- {
- count5ms = 0;
- flag50ms = 1;
- }
-
- get10s++;
- sum = sum + ADconv();
- if (get10s == 10)
- {
- get10s = 0;
- ET0 = 0;
- TR0 = 0;
- flagget10s = 1;
- sum = sum / 10;
- }
-
- if (key1==0)
- {
- while (!key1);
- Wh++;
- if (Wh >=51)
- {
- Wh = 40;
- }
- }
- if (key2==0)
- {
- while (!key2);
- Wl++;
- if (Wl >=Wh)
- {
- Wl = 20;
- }
- }
- if (key3==0)
- {
- while (!key3)
- Alarmflag = ~Alarmflag;
- }
- }
- }
-
- /*
- void Process50ms(void)
- {
- if (flag50ms == 1)
- {
- flag50ms = 0;
- Led = ~Led;
-
-
- }
- }
- */
-
- void CheckProcess()
- {
- uint16 Wig, SetH, SetL;
- Wig = (uint16)temp_zheng*100 + (uint16)(temp_xiao*100);
- SetH = (uint16)Wh*100;
- SetL = (uint16)Wl*100;
- if ((Wig>SetH) && (Alarmflag==1))
- {
- Beep = 0;
- }
- else if ((Wig<SetL) && (Alarmflag==1))
- {
- Beep = 0;
- }
- else if (Alarmflag == 0)
- {
- Beep = 1;
- }
- else
- {
- Beep = 1;
- }
-
- }
- /*
- uchar Get10sAD(void)
- {
- uchar i;
- uint sum = 0;
-
- for (i=0; i<10; i++)
- {
- sum = sum + ADconv();
- delay_ms(10);
- }
- sum = sum/10;
- return sum;
- }
-
-
- */
- void Get_temp(uint ad_temp)
- {
- uint8 n = 0;
- while(1)
- {
- if ((ad_temp >= AD_Tab[n]) && (ad_temp <= AD_Tab[n+1]))
- {
- temp_zheng = n + 10;
- temp_xiao = (1.0*(ad_temp - AD_Tab[n]))/(AD_Tab[n+1] - AD_Tab[n]);
- break;
- }
- n++;
- if (n>40)
- {
- break;
- }
- }
- }
-
- void Disp_Voltage(void)
- {
- uchar temp;
- temp = (uchar)(temp_xiao * 100);
- Prints("Weight:"); //提示语
- PutChar_to_Uart(temp_zheng/100 + 0x30);
- PutChar_to_Uart(temp_zheng%100/10 + 0x30);
- PutChar_to_Uart(temp_zheng%100%10 + 0x30);
- PutChar_to_Uart('.');
- PutChar_to_Uart(temp/10 + 0x30);
- PutChar_to_Uart(temp%10 + 0x30);
- PutChar_to_Uart('T');
- PutChar_to_Uart(0x0d); //换行
- PutChar_to_Uart(0x0a); //换行
- Write_char(temp_zheng/100 + 0x30, 0, 8);
- Write_char(temp_zheng%100/10 + 0x30, 0, 9);
- Write_char(temp_zheng%100%10 + 0x30, 0, 10);
- Write_char('.', 0, 11);
- Write_char(temp/10 + 0x30, 0, 12);
- Write_char(temp%10 + 0x30, 0, 13);
- Write_char('T', 0, 14);
- }
-
-
- void main(void)
- {
-
- InitUART(); //串口初始化
- Lcd_init();
- Write_String("Weight: ", 0, 0);
- Write_String("H=", 1, 0);
- Write_String("L=", 1, 6);
- Beep = 1;
- while(1)
- {
- Process10ms();
- DispVal(Wh, 1, 2);
- DispVal(Wl, 1, 8);
- CheckProcess();
- if (flagget10s == 1)
- {
- flagget10s = 0;
- Get_temp(sum*100);
- ET0 = 1;
- TR0 = 1;
- Disp_Voltage(); //采集电压并发送
- }
-
- }
- }
-
- void Timer0() interrupt 1
- {
- static uint8 count1ms = 0;
- TH0 = (65535 - 1000)/256;
- TL0 = (65535 - 1000)%256;
- count1ms ++;
- if (count1ms == 10)
- {
- count1ms = 0;
- flag10ms = 1;
- }
-
- }
完整的Word格式文档51黑下载地址: