C语言代码与Proteus原理图在评论区。
要求与主要技术指标
A设计以单片机为核心的电子秤,实现称重功能,精度+/-20克,实现计价,去皮等功能。
B按键必须采用电容触摸按键。
设计主要内容
以STC89C52单片机为控制核心,使用HX711称重模块作为电子秤称重部分,LCD1602作为显示部分,TTP233型电容按键为操作部分。实现了称重、去皮、计价、调整每千克金额的功能。
原理图与设计实物
元件介绍
元件名称 | 图片 | 说明 |
51单片机开发板 | | |
TTP223 电容按键 6件 | | 电容按键与物理按键有着明显区别,电容按键在闲置时间中输出低电平,在按下时输出高电平。 |
74LS04 非门2件 | | 非门的作用是为了翻转电容按键的输出特性,也为了在单片机IO口与电容按键之间形成隔离。 |
HX711 称重模块 | | 其中包含一个应变片式全桥压力传感器和HX711 高精度24位AD转换芯片。 |
LCD1602 | |
设计原理
称重原理
重力传感器的应变片布置与接线方式如下图所示。
如图所示在悬臂梁上方的应变片会由于受力产生拉伸使得阻值增加,悬臂梁下方的应变片在受力后会收缩导致阻值减小,由于应变片阻值的增加与减小与受力大小呈一个函数关系,在接线如右图的情况下,就可以通过测量 上的电压值来间接测量受力值,基本原理如下。
HX711高精度24位AD
本次设计实验中的HX711 采用了海芯科技集成电路专利技术,是一款专为高精度电子秤而设计的 24位A/D转换器芯片。与同类型其它芯片相比,该芯片集成了包括稳压电源、片内时钟振荡器等其它同类型芯片所需要的外围电路,具有集成度高、响应速度快、抗干扰性强等优点。降低了电子秤的整机成本,提高了整机的性能和可靠性。
该芯片与后端 MCU 芯片的接口和编程非常简单,所有控制信号由管脚驱动,无需对芯片内部的寄存器编程。输入选择开关可任意选取通道A或通道B,与其内部的低噪声可编程放大器相连。通道A的可编程增益为128或64,对应的满额度差分输入信号幅值分别为士20mV或士40mV。通道B则为固定的32增益,用于系统参数检测。芯片内提供的稳压电源可以直接向外部传感器和芯片内的 A/D 转换器提供申源,系统板上无需另外的模拟电源。芯片内的时钟振荡器不需要任何外接器件。上电自动复位功能简化了开机的初始化过程。芯片的封装与引脚如下图。
在设计实验中,该芯片的接线如下。通过其模拟量输入端与电桥电路相连可以将全桥产生的微小模拟量电压转换成数字量再传送给单片机。
HX711芯片与单片机之间的通讯依靠DOUT与PD_SCK端口,在一个AD转换周期开始前,DOUT端口会保持高电平状态,这时单片机应该处于等待状态。当DOUT端口电平变为低电平时,意味着一个转换周期开始。单片机这时应该开始向HX711芯片的PD_SCK端口发送脉冲,在脉冲为高电平的时刻逐位地读取转换值。
读取的数据值为原始模拟量数据与一个增益的乘积,这个增益和其对应的端口选择与一个周期中单片机向HX711芯片发送的脉冲数有关,在本设计中,选择:通道A、增益128。下图为芯片数据手册中提供的时序示意图,从中也可以看出数据的传输是从高位开始的。
电容按键工作原理
在任何两个导电的物体之间都存在电容,电容的大小与介质的导电性质、极板的大小与导电性质、极板周围是否存在导电物质等有关。PCB 板上集成了两个极板,等于一个电容器。当人体的手指接近PCB 时,由于人体的导电性,会改变电容的大小。触摸按键芯片检测到电容值大幅升高后,输出开关信号。下图形象的展示了上述过程。
在使用电容按键时,需要格外注意电容按键的输出。与普通的物理按键不同,电容按键在没有被按下时输出低电平,在按下时会输出高电平。这就需要在硬件设计上或者在程序设计上做出响应的调整。
原理图搭建与功能模块介绍
使用Proteus8.6搭建本设计原理图,包括主控芯片、模拟电容按键、蜂鸣器模块、HX711模块、LCD1602显示模块这五个部分;原理图全图如下。
主控芯片采用Proteus8.6元件库中的AT89C52来代替STC89C52进行仿真,对于本次设计实验中两个芯片在性能与功能上没有区别。
模拟电容按键模块主要是为了模拟电容触摸按键的输出特性。实验中采用的TTP233电容按键在按键触发时输出高电平,在闲置时输出低电平。因此不能使用简单的物理按键BUTTON来进行仿真。所以设计如下的仿真原理图。
该结构用元件库中的物理按键模型模拟了电容触摸按键的输出,在单片机与电容按键之间增加了74LS04非门,这样起到既起到了隔离作用使电容按键与单片机不相互干扰,也使得电容按键的输出对于单片机检测来说等效于物理按键。
共设计有电容按键8个,其中有两个按键用来控制是否去皮与是否调整单价。其余六个按键用来对单价的十位、个位、毛位进行加减。
蜂鸣器模块用来模拟开发板上的蜂鸣器。在原理图的搭建过程中,需要注意在Proteus8.6中要选择有源蜂鸣器,并且使用三极管进行驱动,如下图所示,同时还要注意将蜂鸣器的工作电压跳到一个与电源匹配的状态。本设计中调至3V。
| |
蜂鸣器会对用户进行提示,在开启、取消去皮功能、调整单价时每单片机每响应一个电容按键触发就会伴随蜂鸣器响一下。
HX711模块用来模拟称重模块,其原理图如下,当按下+按键时会使得该模块输出的质量增加,按下-键会使得输出质量减小。
LCD1602显示模块,原理图如下,显示当前质量(左上)、单价(右上)、总价(左下)与是否去皮(右下)。在去皮与调整单价状态下有状态提示字符。
| |
去皮状态 | 调价状态 |
| |
在LCD1602模块中,本人注重对课上所学内容的应用,“元”、“去”、“皮”三个字符为自建库中的字模。取字模的过程如下。
|
|
|
程序
本设计的程序编写在keil软件中完成,在工程文件中分三个C文件编写程序。分别为主函数main.c,显示器相关函数1602.c,AD转换模块相关函数 HX711.c。
main.c
#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define out P0
sbit RS=P1^0; //定义与LCD1602控制相关的端口
sbit RW=P1^1; //与本人开发板上的端口定义一致
sbit E=P2^5;
sbit DOUT=P2^0; //定义与HX711模块相关的端口
sbit SCK=P2^1;
void delay(uint j);//函数定义
void check_busy(void);
void write_command(uchar com);
void write_data(uchar dat);
void initial(void);
void string(uchar ad,uchar *s);
unsigned long ReadCount(void);
void cal_tram(long cal);
void writenum(uchar location);
void writejine(uchar location);
void Get_Maopi();
void jine();
void writedanjia(uchar location);
void sound();
uchar code num[10]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};//数字0-9对应的CGROM位置
uchar code yuan[8]={14,0,31,10,10,10,19,0};//储存“元”字模
uchar code qu[8]={4,14,4,31,4,9,31,0}; //储存“去”字模
uchar code pi[8]={4,31,21,30,26,20,26,17}; //储存“皮”字模
uchar numshow[4]={0,0,0,0}; //储存数字显示的编号
uchar numjine[7]={0,0,0,0,0,0,0};
uchar numdan[3]={0,0,0};
uchar j;
unsigned long count; //定义变量
unsigned long MP;
unsigned long MPr;
unsigned long money;
unsigned long danjia;
unsigned long zhiliang;
unsigned long rezhiliang;
unsigned long redanjia;
unsigned long remoney;
sbit DD=P2^0; //有关单片机去皮调金额的一些交互用端口的定义
sbit DP=P2^4;
sbit GD=P2^2;
sbit GP=P3^0;
sbit SD=P3^1;
sbit SP1=P3^4;
sbit QU=P3^2;
sbit TIAO=P3^3;
sbit speak=P2^3;
sbit shuma1=P2^6; //这两个端口在开发板上与数码管显示相关
sbit shuma2=P2^7; //为了关闭数码管使得LCD1602显示稳定
bit flagq; //标志位为了调整金额与去皮
bit flagt;
void main()
{
shuma1=shuma2=0;//屏蔽数码管
initial(); //初始化LCD1602
delay(5);
write_command(0x40); //写入汉字元、去、皮的字模
delay(1);
for(j=0;j<8;j++)
{
write_data(yuan[j]);
delay(1);
}
write_command(0x40+8);
delay(1);
for(j=0;j<8;j++)
{
write_data(qu[j]);
delay(1);
}
write_command(0x40+16);
delay(1);
for(j=0;j<8;j++)
{
write_data(pi[j]);
delay(1);
}
rezhiliang=5555; //初始化几个记录的值,定义了几个溢出的值
redanjia=11111; //其目的是为了在当上电未进行称重时显示0
remoney=111111;
Get_Maopi(); //获取初始毛皮
MPr=MP; //因为设置了去皮与不去皮两种模式因此会记录下最开始的毛皮
flagq=flagt=0;
danjia=100;//初始单价
write_command(10+0xc0); //初始化写入固定位置的不变字符
write_data(1);
write_command(11+0xc0);
write_data(2);
string(12+0xc0,":");
string(0x80,"w:");
string(0xc0,"M:");
string(0x8d,"/kg");
while(1)
{
if(redanjia!=danjia) //以下的if判断功能,示数不变化就不重复写入LCD1602
{ //对于总金额与称重的结果,保证结果稳定不跳变
redanjia=danjia;
writedanjia(8+0x80); //显示每千克的单价
}
count=ReadCount();
cal_tram(count);
if((rezhiliang>zhiliang+1)||(rezhiliang<zhiliang-1)) //显示质量
{
rezhiliang=zhiliang;
writenum(0x82);
}
money=zhiliang*danjia;
if((remoney>money+10)||(remoney<money-10)) //显示总的金额
{
remoney=money;
jine();
writejine(0xc2);
}
if(QU==0) //去皮
{
delay(40);
if(QU==0)
{
flagq=~flagq;
if(flagq==1)
{
Get_Maopi(); //去皮模式
string(13+0xc0,"#");
}
if(flagq==0)
{
MP=MPr; //恢复为整体的质量
string(13+0xc0," ");
}
}
sound();
delay(10);
while(QU==0);
}
if(TIAO==0)//调整单价
{
delay(50);
if(TIAO==0)
{
flagt=1;
string(0x87,"#");
sound();
while(TIAO==0);
while(flagt)
{
if(DD==0) //调整毛位单价
{
danjia=danjia-1;
sound();
while(DD==0);
}
if(DP==0)
{
danjia=danjia+1;
sound();
while(DP==0);
}
if(GD==0) //调整个位单价
{
danjia=danjia-10;
sound();
while(GD==0);
}
if(GP==0)
{
danjia=danjia+10;
sound();
while(GP==0);
}
if(SD==0)
{
danjia=danjia-100;
sound();
while(SD==0);
}
if(SP1==0) //调整十位单价
{
danjia=danjia+100;
sound();
while(SP1==0);
}
if(danjia>999)
{
danjia=0;
}
writedanjia(8+0x80);
if(TIAO==0) //用来判断调整结束的
{
delay(50);
if(TIAO==0)
{
flagt=0;
string(0x87," ");
sound();
while(TIAO==0);
}
}
}
}
}
}
}
void cal_tram(long cal) //对质量数据进行数位转换
{
cal=cal-MP;
if(cal<0) //保证不出现由于减法或者去皮模式下拿走毛皮出现质量错误
{cal=0;}
cal=(unsigned int)((float)cal/466);
//numshow的低位为cal数据的高位
numshow[0]=cal/1000;
cal=cal-1000*numshow[0];
numshow[1]=cal/100;
cal=cal-100*numshow[1];
numshow[2]=cal/10;
cal=cal-10*numshow[2];
numshow[3]=cal;
zhiliang=numshow[0]*1000+numshow[1]*100+numshow[2]*10+numshow[3];
}
void writenum(uchar location)//显示质量数据
{
uchar i;
for(i=0;i<4;i++)
{
write_command(location);
write_data(num[numshow[i]]);
location=location+1;
}
string(0x86,"g");
}
void Get_Maopi()//获取毛皮数据
{
MP=ReadCount();
}
void jine()//对金额进行数位转换
{
numjine[0]=money/1000000;
money=money-1000000*numjine[0];
numjine[1]=money/100000;
money=money-100000*numjine[1];
numjine[2]=money/10000;
if(numjine[2]>=7)
{
money=money-(numjine[2]-3)*10000;
numjine[3]=(money/1000)-30;
}
else
{
money=money-(numjine[2])*10000;
numjine[3]=(money/1000);
}
}
void writejine(uchar location)//显示金额
{
uchar i;
for(i=0;i<4;i++)
{
if(i==3)
{
string(location,".");
location=location+1;
}
write_command(location);
write_data(num[numjine[i]]);
location=location+1;
}
write_command(0xc7);
write_data(0);
}
void writedanjia(uchar location)//对单价进行数位转换并显示
{
uchar i;
uint t;
t=danjia;
numdan[0]=t/100;
t=t-100*numdan[0];
numdan[1]=t/10;
t=t-10*numdan[1];
numdan[2]=t;
for(i=0;i<3;i++)
{
if(i==2)
{
string(location,".");
location=location+1;
}
write_command(location);
write_data(num[numdan[i]]);
location=location+1;
}
write_command(0x8c);
write_data(0);
}
void sound()//蜂鸣器响一声
{
speak=0;
delay(75);
speak=1;
}
主函数流程图
1602.c
#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define out P0
sbit RS=P1^0;
sbit RW=P1^1;
sbit E=P2^5;
void delay(uint j) //延时函数
{
uchar i=250;
for (;j>0;j--)
{
while(--i);
i=249;
while(--i);
i=250;
}
}
void check_busy(void) //读忙函数
{
uchar dt;
do
{
dt=0xff;
E=0;
RS=0;
RW=1;
E=1;
dt=out;
}while(dt&0x80);
E=0;
}
void write_command(uchar com) //写命令函数
{
check_busy();
E=0;
RS=0;
RW=0;
out=com;
E=1;
delay(1);
E=0;
delay(1);
}
void write_data(uchar dat) //写数据函数
{
check_busy();
E=0;
RS=1;
RW=0;
out=dat;
E=1;
delay(1);
}
void initial(void) //初始化函数
{
write_command(0x3f);
delay(1);
write_command(0x0c);
delay(1);
write_command(0x06);
delay(1);
write_command(0x01);
delay(1);
}
void string(uchar ad,uchar *s) //写字符函数
{
write_command(ad);
while(*s>0)
{
write_data(*s++);
delay(1);
}
}
HX711.c
#include<reg52.h>
#include<intrins.h>
void Delay__hx711_us(void)
{
_nop_();
_nop_();
}
sbit DOUT=P3^5;
sbit SCK=P3^6;
unsigned long ReadCount(void)
{
unsigned long Count;
unsigned char i;
SCK=0; //使能AD(PD_SCK 置低)
Count=0;
while(DOUT); //AD转换未结束则等待,否则开始读取
for (i=0;i<24;i++)
{
SCK=1; //PD_SCK 置高(发送脉冲)
Delay__hx711_us();
Count=Count<<1; //下降沿来时变量Count左移一位,右侧补零
SCK=0; //PD_SCK 置低
Delay__hx711_us();
if(DOUT) Count++;
}
Delay__hx711_us();
SCK=1;
Delay__hx711_us();
Count=Count^0x800000;//第25个脉冲下降沿来时,转换数据
SCK=0;
return(Count);
}
HX711工作流程图
综合设计作品展示
未去皮状态下称重 |
|
去皮模式下称重 |
|
每千克单价调整 |
|