您好,欢迎来电子发烧友网! ,新用户?[免费注册]

当前位置:电子发烧友网 > 图书频道 > 电子 > 《单片机原理与应用》 > 第8章 单片机高级程序设计

第3节 计重计数电子秤

  8.3.1 电子秤技术指标和功能要求

  1. 预存10组储单重值,方便调出。

  2. 计数:能已知单重求个数,已知个数求得单重再计数。

  3. 去皮:已知皮重扣除指定的皮,一次性扣除秤上的皮。

  4. 零点:偶尔会发生零点漂移现象,可按零点回零。

  5. 累计:个数,重量。

  6. 预先设定个数上下限值,重量上下值。超过上下限自动报警

  7. 单位转换:千克,英磅,盎司,金衡盎司,克拉,英钱,克。

  8. 开关背光,蜂鸣器。

  9. 外部进行校正(在秤发生漂移时,可以进行校正)。

  10. 精度可达0.1,0.2,0.5,1,2,5,10,20,50g等可调(通过调软件,换传感器达到)。

  11. 外码可达60000(通过软件设置,可达几十种不同规格的量程和精度的电子秤)。

  12. 电源检测

  13. 当电池电压下降至5.25 0.1V,电源指示灯红灯亮,则表示要充电。若此时不充电,将有可能因电压太低而出现称量不准或不稳定。如继续使用超过2小时,电子天平将自动关机,进入保护模式。

  14. 传感器:电阻应变式秤重传感器。

  15. 额定电压:

  16. 交流 AC 220V(+10%-15%)50Hz 1Hz;

  17. 直流 DC6V/4AH充电电池(充电式)。

  18. 贮藏:-40~60 19. 工作:5~40 20. 湿度:

  21. 贮藏:<=70%RH无结露;

  22. 工作:<=90%RH无结露。

  23. 内部采用20位专用AD。

  8.3.2 功能详细说明

  1. 单重(个数)预设

  (1) 如何预设单重(个数)

  1) 利用数字键0~9输入单重值(个数)。

  2) 按“单重预设”键。个数栏显示“PrSET”。

  3) 按“单重预设”键。重量栏显示“SET”。

  4) 按0~9任何一键,即可将单重(个数)存入此键。

  (2) 如何叫出所预设之单重(个数)

  1) 按“单重预设”键。个数栏显示“PrSET”。

  2) 按0~9任何一键,即可将此键内之单重(个数)叫出。

  说明:个数最多为65545个以下,重量的小数最多为两位。

  2. 计数

  在计算数量前,必须先作取样步骤以求得待称物品之单重。取样方法有下列2种。

  (1) 待秤物品之单重未知

  1) 将一取样物品置于天平盘上。

  2) 输入天平上取样物品之数量。

  3) 按“个数设定”键。

  4) 当电子秤稳定后,即取样完成进入计数模式。

  (2) 待秤物品之单重已知

  1) 输入已知的待秤物品之单重 ,进入计数模式。

  2) 取样之数量愈大,所计算出之单重愈精确。

  3. 扣除包装物品之重量

  (1) 包装物品置于天平盘上。

  (2) 按“去皮”键。重量栏显示“tArE”。

  (3) 当电子秤稳定后,即进入计数模式。

  将天平上物品与包装一并移开后,重量栏将显示包装物品重量之负值,此时再按一次“去皮”键,即可取消去皮,使重量归零且去皮符号“p”消失。

  4. 零点

  电子秤在工作过程中,偶尔会发生零点漂移现象,(即重量栏之重量有微小变动)此时按“零点”键可使重量回复。

  5. 累计

  (1) 将物品置于天平盘上。

  (2) 按“累计”键。

  (3) 当电子秤稳定后。重量栏显示总重量,个数栏显示出总个数,单重栏显示总笔数。

  (4) 约3秒后,电子秤回复计数模式。

  累计笔数最多为99笔,但位数为6位。

  按“累计清除”键,即可将记忆中之累计值清除且累计符号“p”消失。

  6. 数量预设(括号中为下限值)

  计数时可预先设定数量之上(下)限值,以后每次计算数量,若超过(少于)此数值即有警告声且单重栏有­—o.ty­­­­­­—(—SLX--)字样闪动。

  7. 如何预设数量之上(下)限值

  (1) 天平盘上有无物品皆可,请按“数量预设”键。个数栏出现“SLYS”

  (2) 按数字键“1”(“3”),重量栏出现“SZ---1”(“SZ---3)。

  (3) 输入欲设定之上(下)限值。(可利用“清除”键修改所输入之数值)

  (4) 按“个数设定”键。(可利用“清除”键修改所输入之数值。

  (5) 按“数量预设”键。电子秤回复计数模式。

  8. 重量预设

  可预先设定重量之上(下)限值,以后每次秤重时,若超过(少于)此数值即有警告声且单重栏有uuPST字样闪动。

  9. 如何预设数量之上(下)限值

  (1) 秤盘上有无物品皆可,请按“数量预设”键。个数栏出现“SLYS”

  (2) 按数字键“2”(“4”),重量栏出现“SZ---2”(“SZ---4)。

  (3) 输入欲设定之上(下)限值。(可利用“清除”键修改所输入之数值)

  (4) 按“单重设定”键。(可利用“清除”键修改所输入之数值。

  (5) 按“数量预设”键。电子秤回复计数模式。

  10. 清除所预设之上(下)限值

  欲清除所设数量或重量之上(下)限值,请依上述预设步骤操作,在输入预设时,则输入“0”即可。

  说明:所设定的数字为重量栏所要出现的数字,不包括小数点,如:量程为3千克,精度为0.1,(3000.0),设定上限值为2000克,即输入20000即可。

  11. 单位切换

  先按长按“清除”键,直到出现CENTR,接着再按“0”,接着按“.”键,会在“Kg,ct,lb,oz,ozt,dwt,g”之间切换,确认好所选单位后,按“去皮”键确认。

  12. 背光

  13. 自动背光模式

  先按长按“清除”键,直到出现CENTR,接着再按“5”

  当天平盘上放置物品时,背光点亮,按按键时,(背光亦点亮,待归零后约5秒背光熄灭。)

  14. 一般背光模式

  先按长按“清除”键,直到出现CENTR,接着再按“4”,背光一直点亮。

  15. 取消背光

  先按长按“清除”键,直到出现CENTR,接着再按“6”,开机后电子秤将记忆住所选用之背光模式,待下次开机后,(维持相同之背光模式。)

  16. 蜂鸣器

  先按长按“清除”键,直到出现CENTR,按“2”,表示按键也有声音发出。如是按“3”,表示按键没有声音发出。

  开机后电子秤将记忆住所选用之蜂鸣模式, 待下次开机后,(维持相同之蜂鸣模式。)

  17. 外部校正

  (1) 按长按“清除”键,直到出现CENTR,按“8”,个数栏出现“CAL”,个数栏出现“999”。

  (2) 按“去皮”确认进入标定零点状态,(按“清零”键退出零点标定。)红灯亮。在单重栏会出现当前的内码值。内码确定好以后,绿灯亮,个数栏出现“989”,按“去皮”键确认,进入上限标定状态。按“清零”键放弃存入内码值,进入上限标定状态。

  (3) 显示“000000”,按“累计”切换闪炫的位,按“.”闪炫位加一。注意:标定时,砝码不能大于最大量程,最小不能小于分度数。设定好后,按“去皮”进入取码状态,红灯亮。内码确定好后,绿灯亮,个数栏出现“888”。按“去皮”确认,自动重新初始化。(也可按“清零”键取消退出或放弃存入内码值。

  18. 调入出厂之校正值

  按长按“清除”键,直到出现CENTR,按“8”,即可,标定好以后,自动重新初始化。

  19. 标定与调试

  在显示启动画面时,打开标定开关,可进入标定与调试状态。

  (1) 分度数选择:显示[N ****]

  按“.”键,显示的****字为,3000, 6000,7500,10000,12000,15000,20000,30000,60000,100000循环改变,选择其中一项,按“去皮”键确认,自动进入下一步骤。如该项不要改变,则按“清除”键进入 下一步骤。

  (2) 分度值选择:显示[E *]

  按“.”键,显示的*字为,1,2,5,10,20,50循环改变,选择其中一项,按“去皮”键确认,自动进入下一步骤。如该项不要改变,则按“清除”键进入下一步骤。

  (3) 小数点选择:显示[D 0.0]

  按“.”键,显示的数字为,0,0.0,0.00,0.000,0.0000循环改变,选择其中一项,按“去皮”键确认,自动进入下一步骤。如该项不要改变,则按“清除”键进入 下一步骤。

  (4) 满量程显示:显示[***.**]

  将上面确认的分度数(例如:3000)乘以分度值(例如:5),再配以小数点(例如:0.00),使用户确认是否要高置的满量程值(例如:150.00),如不是,把标定开关关上再打开即可重新标定上述三步。如是,则按“清除”键进入下一步骤。

  (5) 零跟踪范围的确定:显示[01 *.*]

  按“.”键,显示的*.*字为,0 .5,1 .0,2 .0循环改变,表示范围为:0 .5,1E,2E,选择其中一项,按“去皮”键确认,自动进入下一步骤。如该项不要改变,则按“清除”键进入下一步骤。

  (6) 开机判零范围:显示[02 ***]

  按“.”键,显示的*.*字为,0 .10,0 .20,1.00循环改变,表示当零位不要保存时(后面的步骤8中期4设为0),开机时称量值如在上述设定的10%FS,20%FS,100%FS范围内,称量值置零,否则以原来储存的零位作为开机时的零位。选择其中一项,按“去皮”键确认,自动进入下一步骤。如该项不要改变,则按“清除”键进入下一步骤。

  (7) 手动置零范围选择:显示[05 .**]

  按“.”键,数字在0.02,0.04,0.08,1.00循环,表示范围为2%,4%,8%,100%F.S。选择其中一项,按“去皮”键确认,自动进入下一步骤。如该项不要改变,则按“清除”键进入 下一步骤。

  (8) 滤波常数的选择:显示为[LB *]

  按“.”键,数字在1,2,3,4,5 ,6, 7, 8, 9, 10循环,分别表示滤波常数为40,60,80,100,120数字越大,刷新速度越慢,但稳定性愈好。选择其中一项,按“去皮”键确认,自动进入下一步骤。如该项不要改变,则按“清除”键进入下一步骤。

  (9) 零位的确定:显示[CAL ]两秒钟,再显示[NOLOAD]

  检查秤上是否空,显示“999”,稍等十秒钟左右,让秤加零稳定,显示“989”,按“去皮”键确认,自动进入下一步骤。如该项不要改变,则按“清除”键进入 下一步骤。

  (10) 满值的标定(标定的砝码越接近或等于满值越好)。显示加数砝码为[000000](此处应说明以什么为单位输入),最左一位闪烁。

  按“累计”键可向右循环改变闪烁位。按“.”键闪烁位加1。逐位打入实际加载的砝码数。按“去皮”键确认,显示“898”,稍等10秒~20秒钟左右,让秤加零稳定,显示“888”,(如重量值太小,则显示[END ]约3秒钟,再重复上述过程),最后显示为[END]。如该项不做,则按“清除”也可。

  (11) 关上标定开关,标定结束。(我们用的不是标定开关而是跳线,标定时跳线在ADJ,不标定时跳线在LOCK)

  8.3.3 详细设计

  1. 硬件设计

  (1) 主要原器件

  89C52、CS5513、OPA2277、CSI93C46、HT1622等

  (2) 硬件原理图

  1) 键盘原理图:

  按键分布与键面文字:

  2) LCD部分原理图及MCU部分电路:

  2. 程序代码

  (1) 软件初始化与LCD

  LCD驱动芯片HT1622介绍:

  其它读写时序可以到网上下载相应资料。

  #include "head.h"

  const UINT8 Number[20]=

  {0xbe,0x06,0x7c,0x5e,0xc6,0xda,0xfa,0x0e,0xfe,0xde,0xbf,0x07,0x7d,

  0x5f,0xc7,0xdb,0xfb,0x0f,0xff,0xdf};

  data UINT8 LcdRam[18];

  data char Slsdcs; //计录“数量设定次数?

  data uchar Dzz; //计录“单重值”

  data char Wpsl; //计录“物品数量?

  data char Ljcs; //计录“累计次数”

  data char Dssl; //计录“定数数量”

  data uchar Dszl; //计录“定数重量”

  data uchar Qpz; //计录“去皮值”

  //data float zl[10]; 用来存放“单重预设”值

  /*

  LcdDATSet1(13,0x01);

  LcdDATSet1(13,0x02);

  LcdDATSet1(13,0x04);

  LcdDATSet1(13,0x08);

  LcdDATSet1(14,0x04);

  LcdDATSet1(14,0x08); 显示 每个数字底下的“▽”

  LcdDATSet1(14,0x02); 显示 “k”

  LcdDATSet1(14,0x01); 显示 “g”

  LcdDATSet1(15,0x08); 显示 “oz"

  LcdDATSet1(14,0x04); 显示 “t"

  LcdDATSet1(14,0x02); 显示 "lb"

  LcdDATSet1(14,0x01); 显示 "dwt"

  LcdDATSet1(15,0x08); 显示 "ct"

  LcdDATSet1(14,0x04); 显示 "wot"

  LcdDATSet1(14,0x02); 显示 "tl"

  LcdDATSet1(14,0x01); 显示 "pcs"

  LcdDATSet1(14,0x08); 显示 "%"

  LcdDATSet1(14,0x04); 显示 " "

  LcdDATSet1(14,0x02); 显示 "-"

  LcdDATSet1(14,0x01); 显示 "◎"

  LcdDATSet1(7,0x0b);

  LcdDATSet1(8,0x0e);

  LcdDATSet1(9,0x0e);

  LcdDATSet1(10,0x08); 显示 ”off"

  */

  //const UINT8 a[5]={0xec,0x60,0xc6,0xf8,0xf0}; //显示 "pnset",按“单重预设”所出现的

  //const UINT8 b[5]={0xc6,0xee,0x62,0x62,0xec}; //显示 "5annp",按“个数设定”所出现的

  //const UINT8 c[4]={0xf0,0xee,0x60,0xf8}; //显示 "tane",按“去皮”所出现的

  //const UINT8 d[5]={0xec,0x60,0xf8,0xf0,0xee}; //显示 "pneta",按“去皮”所出现的

  void LcdInit(void)

  {

  LcdCmdSet1(0x01);

  LcdCmdSet1(0x03);

  Delay1(100);

  ClearScreen();

  Delay1(50);

  }

  void ClearScreen(void)

  {

  LcdDATSet1(1,0x00);

  LcdDATSet1(2,0x00);

  LcdDATSet1(3,0x00);

  LcdDATSet1(4,0x00);

  LcdDATSet1(5,0x00);

  LcdDATSet1(6,0x00);

  LcdDATSet1(7,0x00);

  LcdDATSet1(8,0x00);

  LcdDATSet1(9,0x00);

  LcdDATSet1(10,0x00);

  LcdDATSet1(11,0x00);

  LcdDATSet1(12,0x00);

  LcdRam[1]=0x00;

  LcdRam[2]=0x00;

  LcdRam[3]=0x00;

  LcdRam[4]=0x00;

  LcdRam[5]=0x00;

  LcdRam[6]=0x00;

  LcdRam[7]=0x00;

  LcdRam[8]=0x00;

  LcdRam[9]=0x00;

  LcdRam[10]=0x00;

  LcdRam[11]=0x00;

  }

  void DisplayNumber(UINT8 who,float num)

  {

  char temp1;

  char j;

  char l;

  int sss=30000;

  float temppara;

  char n[11];

  temppara=num;

  sss=(int)temppara;

  if(temppara>0)

  {

  for (j=6;j>0;j--)

  {

  temp1=(sss%Pow(10,j))/Pow(10,j-1);

  l=abs(j-6);

  n[l]=temp1;

  }

  for (j=1;j<6;j++)

  {

  temp1=((sss*Pow(10,j))%10)/1;

  l=abs(6+j);

  n[l]=temp1;

  }

  j=-1;

  while (n[++j]!=0)

  {

  }

  for (temp1=j;temp1

  {

  }

  }

  else if(temppara==0)

  {

  switch (who)

  {

  case 1:

  {

  LcdDATSet1(11,0x0b);

  LcdDATSet1(12,0x0e);

  }

  case 2:

  {

  LcdDATSet1(11,0xb0);

  LcdDATSet1(12,0xe0);

  }

  case 3:

  {

  LcdDATSet1(28,0x0b);

  LcdDATSet1(29,0x0e);

  }

  }

  }

  }

  void LcdDATSet1(UINT8 addr,UINT8 dat)

  {

  UINT8 i,temp;

  LCD_CS = 0;

  LCD_SD = 1;

  LCD_CD = 0;

  LCD_CD = 1;

  LCD_SD = 0;

  LCD_CD = 0;

  LCD_CD = 1;

  LCD_SD = 1;

  LCD_CD = 0;

  LCD_CD = 1;

  temp=(addr-1)*2;

  temp=temp<<2;

  for(i=0;i<6;i++)

  {

  if (temp & 0x80)

  LCD_SD = 1;

  else

  LCD_SD = 0;

  LCD_CD = 0;

  LCD_CD = 1;

  temp = temp << 1;

  }

  for (i = 0; i < 8; i++) {

  if (dat & 0x80) LCD_SD = 1;

  else LCD_SD = 0;

  LCD_CD = 0;

  LCD_CD = 1;

  dat = dat << 1;

  }

  LCD_SD=1;

  LCD_CD=0;

  LCD_CD=1;

  LCD_CS= 1;

  }

  void LcdCmdSet1(UINT8 cmd)

  {

  UINT8 i;

  LCD_CD = 1;

  LCD_CS = 0;

  LCD_SD = 1;

  LCD_CD = 0;

  LCD_CD = 1;

  LCD_SD = 0;

  LCD_CD = 0;

  LCD_CD = 1;

  LCD_SD = 0;

  LCD_CD = 0;

  LCD_CD = 1;

  for (i = 0; i < 8; i++) {

  if (cmd & 0x80) LCD_SD = 1;

  else LCD_SD = 0;

  LCD_CD = 0;

  LCD_CD = 1;

  cmd = cmd << 1;

  }

  LCD_SD=0;

  LCD_CD=0;

  LCD_CD=1;

  LCD_CS= 1;

  }

  void disp(UINT8 who,UINT8 n,UINT8 num)

  {

  UINT8 who1,i,dd;

  i=num;

  who1=who;

  dd=n;

  if (who1==1)

  {

  LcdRam[dd*2-2]=(Number[i]>>4)|(LcdRam[dd*2-2]&0xf0);

  LcdRam[dd*2-1]=0x0f&Number[i]|(LcdRam[dd*2-1]&0xf0);

  LcdDATSet1(dd*2-1,LcdRam[dd*2-2]);//(Number[i]>>4)|(LcdRam[dd*2-2]&0xf0));

  LcdDATSet1(dd*2,LcdRam[dd*2-1]);//(0x0f&Number[i])|(LcdRam[dd*2-1]&0xf0));

  }

  if(who1==2)

  {

  LcdRam[dd*2-2]=0xf0&Number[i]|(LcdRam[dd*2-2]&0x0f);

  LcdRam[dd*2-1]=((Number[i]<<4))|(LcdRam[dd*2-1]&0x0f);

  LcdDATSet1(dd*2-1,LcdRam[dd*2-2]);//(0xf0&Number[i])|(LcdRam[dd*2-2]&0x0f));

  LcdDATSet1(dd*2,LcdRam[dd*2-1]);//(0xf0&(Number[i]*16))|(LcdRam[dd*2-1]&0x0f));

  }

  if (who1==3)

  {

  LcdDATSet1(dd*2+16,(Number[i]>>4));

  LcdDATSet1(dd*2+17,(0x0f&Number[i]));

  }

  }

  void Disp9to0(void)

  {

  int i=0,j=0,k=0;

  for (k=0;k<10;k++)

  {

  Delay1(800000);

  for (i=1;i<7;i++)

  {

  disp(2,i,k+10);

  Delay1(80);

  disp(1,i,k+10);

  }

  }

  }

  UINT32 Pow(int n,int m) //n的m次方计算函数

  {

  UINT32 result=1;

  int x,y;

  int i;

  x=n;

  y=m;

  if (y==0)

  return (1);

  else

  {

  for(i=0;i

  result=x*result;

  return (result);

  }

  }

  (2) A/D CS5513

  1) 读数据时序:

  UINT32 CS5513READDATA(void)

  {

  idata UINT8 i;

  idata UINT32 CS5513DATA;

  AD_CS=0;

  AD_CLK=0;

  CS5513DATA=0;

  Delay1(10);

  while(AD_SD !=0) //等待数据转换完成

  {

  }

  for (i=0;i<24;i++)

  {

  CS5513DATA <<=1;

  AD_CLK=1;

  Delay1(10);

  if(AD_SD==1)

  CS5513DATA |= 0x01;

  AD_CLK=0;

  Delay1(10);

  }

  CS5513DATA &=0XFFFFF; //取20位有效数据

  AD_CS=1;

  return(CS5513DATA);

  }

  (3) SPI总线及93C46

  1) SPI总线工作原理

  ① 概述

  CSI93C46/56/57/66/86 是一种存储器,可以定义为16 位ORG 引脚接Vcc, 或者定义为8 位ORG 引脚接GND 的1K/2K/2K/4K/16K 位的串行E2PROM 。每一个的存储器都可以通过DI 引脚或DO 引脚,进行写入或读出。每一片CSI93C46/56/57/66/86 都是采用CSIalyst 公司先进的CMOS E2PROM 浮动门工艺。加工器件可以经受1,000,000 次的写入/擦除操作,片内数据保存寿命达到100 年。器件可提供的封装有DIP-8 SOIC-8 TSSOP-8。

  ② 管脚配置及其方框图

  管脚说明

  说明:当ORG 接Vcc 时存储器为16 位结构

  当ORG 接GND ,是存储器为8 位结构。

  当ORG引脚悬空时,内部的上拉电阻把存储器选择为16 位结构。

  管脚名称功能

  CS 片选信号

  SK 时钟输入

  DI 串行数据输入

  DO 串行数据输出

  Vcc 电源+1.8 伏到6 伏

  GND 接地

  ORG 存储器结构选择

  NC 不用连接

  PE* 写入保护

  图示:数据传输同步时序

  void EEPROMByteWrite0(UINT8 addr,UINT8 value)//写一个字节内容

  {

  EwenROM();

  WriteRom(addr,value);

  EwdsROM();

  SCKPIN=0;

  Delay1(20);

  }

  void EwdsROM(void) //写停止

  {

  SCKPIN=0;

  _nop_();

  CSPIN=1;

  _nop_();

  SDIPIN=1;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  SDIPIN=0;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  SDIPIN=0;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  WR_3wire(0x00);

  SDIPIN=0;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  SDIPIN=0;

  _nop_();

  CSPIN=0;

  _nop_();

  _nop_();

  }

  void WriteRom(UINT8 addr,UINT8 value) //写数据value到地址addr

  {

  UINT16 i;

  SCKPIN=0;

  _nop_();

  CSPIN = 1;

  _nop_();

  _nop_();

  SDIPIN=1;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  SDIPIN=0;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  SDIPIN=1;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  SDIPIN=0;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  WR_3wire(addr);

  WR_3wire(value);

  _nop_();

  CSPIN=0;

  _nop_();

  SDOPIN=1;

  _nop_();

  CSPIN=0;

  _nop_();

  CSPIN=1;

  _nop_();

  for(i=0;i<1000;i++)

  {

  if(SDOPIN || i>=1000)

  break;

  }

  SDIPIN=0;

  CSPIN=0;

  }

  void EwenROM(void) //写允许

  {

  SCKPIN=0;

  _nop_();

  CSPIN=1;

  _nop_();

  SDIPIN=1;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  SDIPIN=0;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  SDIPIN=0;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  WR_3wire(0xc0);

  SDIPIN=0;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  SDIPIN=0;

  _nop_();

  CSPIN=0;

  _nop_();

  _nop_();

  }

  void WR_3wire(UINT8 value)

  {

  UINT8 i;

  for(i=0;i<8;i++)

  {

  if(value&0x80)

  SDIPIN=1;

  else

  SDIPIN=0;

  value<<=1;

  _nop_();

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  _nop_();

  }

  }

  UINT8 EEPROMByteRead0(UINT8 addr) //读ADDR中的内容

  {

  UINT8 value;

  SCKPIN=0;

  _nop_();

  CSPIN=1;

  _nop_();

  SDIPIN=1;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  SDIPIN=1;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  SDIPIN=0;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  SDIPIN=0;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  SCKPIN=0;

  _nop_();

  WR_3wire(addr);

  Delay1(1);

  value=ReceiveByte();

  SCKPIN=0;

  CSPIN=0;

  Delay1(20);

  return value;

  }

  UINT8 ReceiveByte(void)

  {

  UINT8 i;

  UINT8 value=0;

  SDOPIN=1;

  for(i=0;i<8;i++)

  {

  SCKPIN=0;

  _nop_();

  SCKPIN=1;

  _nop_();

  _nop_();

  value<<=1;

  if(SDOPIN) value|=0x01;

  SCKPIN=0;

  }

  _nop_();

  _nop_();

  return value;

  }

  (4) 键盘扫描

  UINT8 Scankey(void)

  {

  UINT8 ee=0,temp1,j;

  Keyval=0;

  temp1=0xff;

  j=0;

  ET0=0;

  sum=0;

  longtime=0;

  while(temp1==0xff)

  {

  j=0;

  temp1=0xff;

  KEY_IN1=0;

  KEY_IN2=0;

  KEY_IN3=0;

  KEY_IN4=0;

  KEY_IN5=0;

  Delay1(1);

  if (KEY_OUT1==0)

  temp1=1;

  if (KEY_OUT2==0)

  temp1=2;

  if (KEY_OUT3==0)

  temp1=3;

  if (KEY_OUT4==0)

  temp1=4;

  KEY_IN1=1;

  KEY_IN2=1;

  KEY_IN3=1;

  KEY_IN4=1;

  KEY_IN5=0;

  Delay1(1);

  if (KEY_OUT1==0 || KEY_OUT2==0 || KEY_OUT3==0 || KEY_OUT4==0)

  j=1;

  KEY_IN1=1;

  KEY_IN2=1;

  KEY_IN3=1;

  KEY_IN4=0;

  KEY_IN5=1;

  Delay1(1);

  if (KEY_OUT1==0 || KEY_OUT2==0 || KEY_OUT3==0 || KEY_OUT4==0)

  j=2;

  KEY_IN1=1;

  KEY_IN2=1;

  KEY_IN3=0;

  KEY_IN4=1;

  KEY_IN5=1;

  Delay1(1);

  if (KEY_OUT1==0 || KEY_OUT2==0 || KEY_OUT3==0 || KEY_OUT4==0)

  j=3;

  KEY_IN1=1;

  KEY_IN2=0;

  KEY_IN3=1;

  KEY_IN4=1;

  KEY_IN5=1;

  Delay1(1);

  if (KEY_OUT1==0 || KEY_OUT2==0 || KEY_OUT3==0 || KEY_OUT4==0)

  j=4;

  KEY_IN1=0;

  KEY_IN2=1;

  KEY_IN3=1;

  KEY_IN4=1;

  KEY_IN5=1;

  Delay1(1);

  if (KEY_OUT1==0 || KEY_OUT2==0 || KEY_OUT3==0 || KEY_OUT4==0)

  j=5;

  if (temp1!=0xff && j!=0 && ee==0)

  {

  Keyval=temp1*5+j;

  j=0;

  temp1=0xff;

  ee++;

  }

  else if (temp1!=0xff && j!=0 && ee==1)

  {

  if (Keyval!=temp1*5+j)

  {

  j=0;

  Keyval=0;

  temp1=0xff;

  ee=0;

  }

  else

  {

  ee++;

  temp1=0xff;

  }

  }

  else if(ee==2 && temp1!=0xff)

  temp1=0xff;

  else if(ee==2 && temp1==0xff && j==0)

  break;

  else if(ee==0 && temp1==0xff && j==0)

  break;

  if(sum==120)

  {

  longtime=1;

  sum=0;

  break;

  }

  }

  if(slys==0)

  ET0=1;

  switch (Keyval)

  {

  case 22:

  if (Once==0)

  Once++;

  break;

  case 21: //数字0

  Keyval=30;

  if (Once>0)

  Once++;

  break;

  case 16: //数字1

  Keyval=31;

  if (Once>0)

  Once++;

  break;

  case 17: //数字2

  Keyval=32;

  if (Once>0)

  Once++;

  break;

  case 18: //数字3

  Keyval=33;

  if (Once>0)

  Once++;

  break;

  case 11: //数字4

  Keyval=34;

  if (Once>0)

  Once++;

  break;

  case 12: //数字5

  Keyval=35;

  if (Once>0)

  Once++;

  break;

  case 13: //数字6

  Keyval=36;

  if (Once>0)

  Once++;

  break;

  case 6: //数字7

  Keyval=37;

  if (Once>0)

  Once++;

  break;

  case 7: //数字8

  Keyval=38;

  if (Once>0)

  Once++;

  break;

  case 8: //数字9

  Keyval=39;

  if (Once>0)

  Once++;

  break;

  default:

  break;

  }

  if(Speakflag==1)

  {

  if(Keyval!=0 && Keyval!=19)

  {

  Speak=0;

  Delay1(100);

  Speak=1;

  }

  }

  return(Keyval);

  }