HSIAO-YANG CHEN 发布的文章

1、增加来去电归属地查询
2、支持音量键唤醒
3、移植CM9的Trebuchet启动器
4、移植CM9的DSP管理器
5、移植Apollo播放器
6、移植下拉快捷开关
7、修改为2%的环形电量指示器
8、增加MIUI文件管理(可以修改系统文件及FTP文件管理)
9、增加Superuser(ROOT管理)
a、增加拨号T9拼音检索联系人
b、移植CM9短信,改“添加联系人”为有手机号的联系人(原为常联系人)
c、精简掉 MusicFX.apk、Maps.apk、Music.apk、Talk.apk
3edd751egcfdd11953615.png
3edd751egcfdd12847076&690.jpg
3edd751eg7b2fb5661606.png
3edd751egcfdd1803ace9.png
3edd751egcfdd18b00972.png
3edd751eg7b2fb5ca4414.png
3edd751egcfdd1bbda128.png
3edd751egcfdd1c710bff.png
3edd751egcfdd1d063069.png
3edd751egcfdd1e50fb93.png
刷机方法参照:小米手机6种刷机详细教程

文件名:mione_plus-ota-QDR68_20121202.signed.zip,访问地址:
http://pan.xiaomi.com/file/id_67101150238736728.htm

从10月份开始做,中间插入做了一个抢答器,走走停停搞到如今总算满意收工。
主要功能:可设置系统参数,可保存调用系统参数。电机通电记录时长,每通电一次自动换一次电压档,检测微动开关状态,检测从微动开关状态,判定时长,通过数码管和LED灯显示,蜂鸣器警报。
原理图:
3edd751egb4adc5fda30c.jpg
程序:

//test.c*/
sfr  WDT = 0xA6;   // 89S52 Watchdog Timer */
#include <intrins.h>
#include <stdio.h>
#include<AT89X52.H>
#include<stdlib.H>
#include <AT24C16.h>
#define uchar  unsigned char
#define  WDTRST WDT=0x1E;WDT=0xE1;
 
#define LEDCode        P0
#define LEDdisplay     P2
#if 0
#define MENU_PIN  P1_4
#define PLUS_PIN  P1_3
#define REDUCE_PIN  P1_2
#define MASTER_PIN     P3_3
#define SLAVE_PIN      P1_2
#define BOARD_PIN      P1_4
#define L1_PIN         P1_0
#define L2_PIN         P1_1
#define L3_PIN         P1_3
#else
 
#define MASTER_PIN     P3_3 //定义通电端口
#define MENU_PIN  P1_7 //定义设置按钮
#define PLUS_PIN  P1_6 //定义加键按钮
#define REDUCE_PIN  P1_5 //定义减键按钮
#define SLAVE_PIN      P1_4 //定义从微动端口
#define BOARD_PIN      P1_3 //定义安装板端口
#define L3_PIN         P1_2   //定义档位端口
#define L2_PIN         P1_1 //定义档位端口
#define L1_PIN         P1_0 //定义档位端口
#endif
#define BELL         P3_7  //蜂鸣器端口
 
  
static uchar code  digital[]= {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xd8,0x80,0x90,0xa0,0xa1,0xa3,0xc7,0x88,0x89,0x91}; //字库码"0,1,2,3,4,5,6,7,8,9,a,d,o,L,R,H,y"
static uchar code select[]={0xff,0xef,0xdf,0xbf,0x7f};  //选位字码
static uchar bod_led[]={0x00,0xfb};   //安装板指示灯 (开关[0],显示代码[1])
static uchar sla_led[]={0x01,0xf7};  //辅助触点指示灯(开关[0],显示代码[1])
static uchar relay[]={0x01,0xff,0xfc,0xfd,0x01};  //继电器三个档位(档位[0],档位代码[1]-[3],记忆上次档位)
static signed char SLa[]={0x01,0x01,0x0a,0x0d,0x05,0x00}; //定义从微动检测(闪动位[0],显示[1]-[4],小数点[5])
static signed char Bod[]={0x01,0x01,0x0b,0x0c,0x08,0x00}; //定义安装板检测
static signed char L1[]={0x03,0x05,0x09,0x00,0x0d,0x02};  //定义第1档最大容忍时间
static signed char L2[]={0x03,0x00,0x06,0x00,0x0e,0x02};  //定义第2档最大容忍时间
static signed char L3[]={0x03,0x00,0x04,0x00,0x0f,0x02};  //定义第3档最大容忍时间
static signed char dis[]={0x00,0x07,0x06,0x10,0x08,0x00};  //分配计时空间
static signed char led[]={0,0,0,0,0}; //计时数据临时存放
static signed char *p=dis; //显示指针
static bit write;//数据保存
static uchar index = 0x01; //起始显示位
static uchar delay;  //等待时间,用于主从微动切换
static bit menu;  //调用设置项
static bit changed;  //选项切换
static unsigned int belltime; //蜂鸣器时长控制
//延迟时长大约为3us*x*/
void delay3xus(int unsigned x)
{
 unsigned int i;
 for(i=0;i<x;i++){WDTRST};
}
//参数读取*/
void read()
{
 uchar i;
 READ_BYTE(5,&SLa[1]);
 READ_BYTE(10,&Bod[1]);
 for(i=1;i<4;i++)
 {
  READ_BYTE(i+15,&L1[i]);
  READ_BYTE(i+20,&L2[i]);
  READ_BYTE(i+25,&L3[i]);
 }
}
//参数保存*/
void save()
{
 uchar i;
 P3= 0xfe;  //设置存储器可写
 EA= 0x00; //禁止其他中断,优先处理此程序
 P2 =0xff;
 P0 = 0xff;
 P1= 0xff;
 WRITE_BYTE(5,SLa[1]);
 delay3xus(500);  //写操作需要等待存储器
 WRITE_BYTE(10,Bod[1]);
 delay3xus(500);
 for(i=1;i<4;i++)
 {
  WRITE_BYTE(i+15,L1[i]);
  delay3xus(500);
   WRITE_BYTE(i+20,L2[i]);
  delay3xus(500);
  WRITE_BYTE(i+25,L3[i]);
  delay3xus(500);
 }
 P3= 0xff;  //设置存储器只读
 EA= 0x01;
}
//初始化*/
void init()
{
 P0  = 0xff;
 P1  = 0xff;
 P2  = 0xff;
 P3  = 0xff;
 EX0  = 0x00;
 ET0  = 0x00;
 EX1  = 0x00;
 ES   = 0x00;
 TL0= 0xa0;  //定时器T0=30ms
 TH0= 0x15;
 TL1= 0xbf;   //定时器T1=4ms
 TH1= 0xe0;
 TMOD = 0x11;  //设置定时器模式
 ET1  = 0x01;   //允许定时器T1中断
 TR1= 0x01;   // 启用定时器T1
 
 TL2= 0xe0;  //10ms
 TH2= 0xb1;
 RCAP2H=TH2;   //中断溢出自动对TH2赋值(T2定时器为16位自动硬件赋值,故不必在中断产生后,再对寄存器再次赋值,但需要对TF2状态进行清零)
 RCAP2L=TL2;   //中断溢出自动对TL2赋值
    T2MOD= 0x00; //设置为定时器
 ET2  = 0x00;   // 允许T2中断
 TR2 = 0x00;    // 启动定时器
 
 
 
 EA = 0x01;  //中断总开关
}
bit shift_voltage();
void clear();
void voltage_led(uchar tmp);
//按键数值加减处理*/
void plus_reduce()
{
 if(!PLUS_PIN)
 {
  delay3xus(25000);
  p[1]+=1;
  if(p[1]>9)
  {
   p[2]+=1;
  if(p[2]>9)
   {
   p[2]=0;
   if(++p[3]>9) p[3]=0;
   }
   p[1]=0;
  }
  write =1;
 }
 else if(!REDUCE_PIN)
 {
  delay3xus(25000);
  p[1]-=1;
  if(p[1]<0)
  {
   p[2]-=1;
  if(p[2]<0)
   {
   p[2]=9;
   if(--p[3]<0) p[3]=9;
     
   }
   p[1]=9;
  }
  write =1;
 }
}
//调用参数设置页
 * menu、changed条件通过T1检测
*/
void setting(void)
{
 if(menu)
 {  
  P3= 0xff;
  delay3xus(0xffff);
  while(menu)
  {
    WDTRST
  p=SLa;   //从微动设置项
   if(!PLUS_PIN)
   {
    delay3xus(25000);
    p[1]=!p[1];
    write =1;   //数据改变,需要更新
   }
   else if(!REDUCE_PIN)
   {
    delay3xus(25000);
    p[1]=!p[1];
    write =1;
   }
   if(changed)
   {
   changed=0;
   if(menu)
    {
    p=Bod;     //安装板设置项
    delay3xus(33333);
    }
   while(menu)
    {
     WDTRST
    if(!PLUS_PIN)
     {
      delay3xus(25000);
      p[1]=!p[1];
     write =1;
     }
    else if(!REDUCE_PIN)
     {
      delay3xus(25000);
      p[1]=!p[1];
     write =1;
     }
    if(changed)
    {
     changed=0;
     break;
    }
    }
   if(menu)
    {
    p=L1;    //第一档时间设置
    delay3xus(33333);
    }
   while(menu)
    {
       WDTRST
      plus_reduce();
    if(changed)
    {
     changed=0;
     break;
    }    
    }
   if(menu)
    {
    p=L2;   //第二档时间设置
    delay3xus(33333);
    }
   while(menu)
    {
     WDTRST
    plus_reduce();
    if(changed)
    {
     changed=0;
     break;
    }    
    }
   if(menu)
    {
    p=L3;    //第三档时间设置
    delay3xus(33333);
    }
   while(menu)
    {
     WDTRST
    plus_reduce();
    if(changed)
    {
     changed=0;
     break;
    }    
    }
    }
    }
   clear();
   voltage_led(relay[0]);
 }
 p=dis;   //退出设置菜单,回到计时状态
 if(write)
 {
   save();
  write=0;   //已保存
  relay[0]=3;  //初始化继电器
 shift_voltage();   //初始化继电器
 clear();   //初始化计时器
 voltage_led(relay[0]);   //初始化继电器
 
 }
 
 
}
//用于主从微动切换等待延迟*/
void start_t0(uchar t)
{
 ET0  = 0x01;
 TR0= 0x01;
 delay=t;
}
//用于主从微动切换等待延迟*/
void stop_t0()
{
 TL0= 0xa0;
 TH0= 0x15;
 TMOD = 0x11;
 ET0  = 0x00;
 TR0= 0x00;
}
//开始计时*/
void start_t2()
{
 ET2  = 0x01;
 TR2 = 0x01; 
}
//停止计时*/
void stop_t2()
{
 TL2= 0xe0;
 TH2= 0xb1;
 ET2  = 0x00;
 TR2 = 0x00;
 
}
//主微动检测,去抖动*/
bit master()   
{
 uchar sum[]={100,300};
 while(sum[0]&&sum[1])  //300*20us
 {
  MASTER_PIN=1;
  _nop_();
  if(MASTER_PIN)
  {
   sum[0]--;
  }
  else
  {
   sum[1]--;
  }
   WDTRST
 }
  return  (bit)sum[0];
}
//从微动检测,去抖动*/
bit slave()
{
 uchar sum[]={100,300};
  while(sum[0]&&sum[1]) // 300*20us
 {
   SLAVE_PIN=1;
  _nop_();
   if(SLAVE_PIN)
  {
   sum[0]--;
  }
  else
  {
   sum[1]--;
  }
    WDTRST
 }
 
 return  (bit)sum[0];
}
//报警蜂鸣*/
void bell()
{
 if(!belltime)
 {
  BELL=0;
  belltime=500; //蜂鸣时长控制
 }
}
//清空计时*/
void clear()
{
 dis[0]=0; //闪动位
 dis[1]=0; //数值1
 dis[2]=0; //数值2
 dis[3]=0; //数值3
 dis[4]=0; //数值4
 dis[5]=3; //标点符号位
}
//处理时长警报*/
void time_chk()
{
 bit overtime=0;
 //超时报警处理 */
 switch(relay[0]) 
 {
  case 0x01:
  if(L1[3]>dis[4]) break;
   else if((L1[3]==dis[4]))
   {
   if(L1[2]>dis[3]) break;
    else if(L1[2]==dis[3])
    {
    if(L1[1]>dis[2]) break;
    else if(L1[1]==dis[2]) break;
    else overtime=1; break;
    }else overtime=1; break;
   }else overtime=1; break;
  case 0x02:
  if(L2[3]>dis[4]) break;
   else if((L2[3]==dis[4]))
   {
   if(L2[2]>dis[3]) break;
    else if(L2[2]==dis[3])
    {
    if(L2[1]>dis[2]) break;
    else if(L2[1]==dis[2]) break;
    else overtime=1; break;
    }else overtime=1; break;
   }else overtime=1; break;
  case 0x03:
  if(L3[3]>dis[4]) break;
   else if((L3[3]==dis[4]))
   {
   if(L3[2]>dis[3]) break;
    else if(L3[2]==dis[3])
    {
    if(L3[1]>dis[2]) break;
    else if(L3[1]==dis[2]) break;
    else overtime=1; break;
    }else overtime=1; break;
   }else overtime=1; break;
    default:
    ;
 }
 if(overtime)
 {
  dis[0]=4;  //计时器闪动
  bell();
 }
}
//换电压档位*/
bit shift_voltage()
{
 char tmp=relay[0]; //程序过程不对继电器状态直接操作,避免继电器抖动
 if(!(L1_PIN&&L2_PIN)) return 0;   //人工操作电压档,不进行换挡操作,直接返回
 if(++tmp>3) tmp=1;   //换挡
 //空档跳过*/
 switch(tmp)
 {
  case 1:
  if(!L1[1]&&!L1[2]&&!L1[3])
   {
    tmp++;
   if(!L2[1]&&!L2[2]&&!L2[3])
    {
    tmp++;
    if(!L3[1]&&!L3[2]&&!L3[3])
    {
     tmp=1;     
    }
    }
   }
   break;
  case 2:
  if(!L2[1]&&!L2[2]&&!L2[3])
   {
    tmp++;
   if(!L3[1]&&!L3[2]&&!L3[3])
    {
    tmp=1;
//    if(!L1[1]&&!L1[2]&&!L1[3])
//    {
//     tmp=1;     
//    }
    }
   }
   break;
  case 3:
  if(!L3[1]&&!L3[2]&&!L3[3])
   {
    tmp=1;
   if(!L1[1]&&!L1[2]&&!L1[3])
    {
    tmp++;
    if(!L2[1]&&!L2[2]&&!L2[3])
    {
     tmp=1;
    }
    }
   }
   break;
  default:;
 }
 relay[4]=relay[0];
 relay[0]=tmp;   //处理换挡完毕,将档位数值写入继电器档位,换挡完成
 return 1;
}
//档位指示灯*/
void  voltage_led(uchar tmp)
{
 switch(tmp)
 {
  case 1:
  P3_4=1;P3_5=0;P3_6=1;break;
  case 2:
  P3_4=0;P3_5=1;P3_6=0;break;
 default:
  P3_4=0;P3_5=0;P3_6=0;break;
 }
 
}
/
bit  bod_open=0;//需要重新调节电压档,临时解除“电机拿出测试台,恢复到首档” ,计时过程开始恢复正常
void  renew()
{
  if(!MENU_PIN) bod_open=0;   //按设置按钮解除“电机拿出测试台,恢复到首档”
 if(Bod[1]&&BOARD_PIN&&L1_PIN&&L2_PIN&&L3_PIN&&bod_open)
  {
   if(L1[1]||L1[2]||L1[3])
    {
    relay[4]=relay[0]=1;
    }
    else if(L2[1]||L2[2]||L2[3])
    {
    relay[4]=relay[0]=2;
    }
    else if(L3[1]||L3[2]||L3[3])
    {
    relay[4]=relay[0]=3;
    }
   else  relay[4]=relay[0]=1;
   voltage_led(relay[0]) ;
   
  }
}
// 人工操作电压档位*/
void manual_control()
{
 char tmp=relay[0];
 if(!L3_PIN)  //档位端口3,设置为第三档
 {
  tmp=3;
  relay[4]=relay[0]=tmp;
  voltage_led(relay[0]) ;
 }
 else if(!L2_PIN)  //档位端口2,设置为第二档
 {
  tmp=2;
  relay[4]=relay[0]=tmp;
  voltage_led(relay[0]) ;
 }
 else if(!L1_PIN)  //档位端口1,设置为第一档
 {
  tmp=1;
  relay[4]=relay[0]=tmp;
  voltage_led(relay[0]) ;
 }
 else if(!PLUS_PIN)
 {
  delay3xus(25000);
  shift_voltage();
  tmp=relay[4]=relay[0];
  voltage_led(relay[0]) ;
 }
 else if(!REDUCE_PIN)
 {
  delay3xus(25000);
  if(--tmp<1) tmp=3;
  switch(tmp)
  {
   case 1:
   if(!(L1[1]||L1[2]||L1[3]))
    {
    tmp=3;
    if(!(L3[1]||L3[2]||L3[3]))
    {
     tmp--;
     if(!(L2[1]||L2[2]||L2[3]))
     {
      tmp=1;
     }
    }
    }
    break;
   case 2:
   if(!(L2[1]||L2[2]||L2[3]))
    {
    tmp--;
    if(!(L1[1]||L1[2]||L1[3]))
    {
     tmp=3;
     if(!(L3[1]||L3[2]||L3[3]))
     {
      tmp=1;
     }
    }
    }
    break;
   case 3:
   if(!(L3[1]||L3[2]||L3[3]))
    {
    tmp--;
    if(!(L2[1]||L2[2]||L2[3]))
    {
     tmp--;
     if(!(L1[1]||L1[2]||L1[3]))
     {
      tmp=1;
     }
    }
    }
    break;
   default:;
  }
  relay[4]=relay[0]=tmp; 
  voltage_led(relay[0]) ;
 }
 
}
//主函数入口,主要过程为控制计时和状态检测*/
void main()
{
 unsigned int i;
 init();  //初始化寄存器
 read();  //读取设置参数
 for(i=2500;i!=0;i--) delay3xus(0xff); //冷开机,比较器输入电容预充电,需要等待
 relay[0]=3;     //初始化继电器参数
 shift_voltage();   //使继电器恢复到首参数档位
 clear();
 voltage_led(relay[0]) ;
 while(1)
 {
  setting() ;  //设置参数
  WDTRST
  renew(); //电机拿出测试台,恢复到首档
    manual_control();  //手工操作电压档
  //主微动闭合,通电开始*/
  if(master())
  {
   start_t2(); //启动计时
  
   voltage_led(relay[0]) ;
   start_t0(35);
  while(delay&&master())
   {
   if(!slave())
    {
    stop_t0();
    break;
    }
    WDTRST
   }
   stop_t0();
     if(!delay&&SLa[1])
   {
   bell();
   }
  
   //主微动闭合,从微动断开,通电过程*/
   while(master())
   {
   if(slave())
    {
    start_t0(35);
    while(delay)
    {
     if(!master())
     {
      break;
     }
      WDTRST
    }
    if(!delay&&SLa[1])
    {
     bell();
     continue;
    }
    }
   }
   stop_t2();
  if(!(led[2]||led[3]||led[4]))
   {
   for(i=1;i<5;i++) led[i]=0;
   voltage_led(relay[4]);
   continue;
   }
   //主微动断开,通电过程结束*/
   start_t0(35);
   while(delay)
   {
    WDTRST
   if(slave())
    {
    start_t0(13);
    while(delay)
    {
     if(!slave()||master())
     {
      stop_t0();
      break;
     }
      WDTRST
    }
    if(master())
    {
     delay=1;
     break;
    }
    else if(delay)
    {
     delay=0;
     break;
    }
    else
    {
     delay=1;
     break;
    }
    }
    else if(master())
    {
      stop_t0();
      break;
    }
   }
  if(!delay&&SLa[1])
   {
    bell();
    }
  
   time_chk();
   shift_voltage();
  for(i=1;i<5;i++) led[i]=0;   //清空临时计时
  bod_open=1;   //通电一次恢复“电机拿出测试台,恢复到首档 ”
  if(Bod[1])   //安装板检测
   {
   if(BOARD_PIN) bell();
   }
  }
   }
}
//定时器T0主要用于主从微动开关延迟等待用*/
void t0() interrupt 1  //30ms
{
 TL0+= 0xa0;
 TH0+= 0x15;
 if(delay>0)
 {
  delay--;
 }
 else
 {
  ET0=0;
  TR0=0;
  TL0= 0xa0;
  TH0= 0x15;
 }
}
/
static uchar skip,count;
void t1() interrupt 3 //4ms
{
 TL1+= 0xbf;
 TH1+= 0xe0;
 sla_led[0]=!SLAVE_PIN;
 bod_led[0]=!BOARD_PIN;
 if(belltime)
 {
  belltime--;
 
 }else BELL=1;
 if(index>p[0])
 {
  LEDCode = digital[p[index]]&(p[5]==index?0x7f:0xff);
  LEDdisplay = select[index]&(bod_led[0]?bod_led[1]:0xff)&(sla_led[0]?sla_led[1]:0xff)&relay[relay[0]];
 
 }
 else if( skip >4 )
 {
  LEDCode = digital[p[index]]&(p[5]==index?0x7f:0xff);
  LEDdisplay = select[index]&(bod_led[0]?bod_led[1]:0xff)&(sla_led[0]?sla_led[1]:0xff)&relay[relay[0]];
  if(index==p[0]) skip=0;
 }
 else
 {
  LEDCode = p[5]==index?0x7f:0xff;
  LEDdisplay = select[index]&(bod_led[0]?bod_led[1]:0xff)&(sla_led[0]?sla_led[1]:0xff)&relay[relay[0]];
 }
 if(++index>4)
 {
  index=0x01;
  skip++;
 }
 if(!MENU_PIN)
 {
  if(count<200) count++;
 }
 else
 {
  if(count==200)
  {
   menu=!menu;
   count=0;
  }
  else if(count>10)
  {
   changed=1;
  }
  count=0;
 }
}
//AT89S52特有的16位中断自动载入TH2、TL2计时器,用于通电时长计时*/
void t2() interrupt 5  //10ms
{
 uchar i;
 TF2 = 0; // 溢出标志必须由软件清零
 EXF2 = 0; // 捕获标志必须由软件清零
 menu=0;//测试过程不可设置
 if(++led[1]>9)
 {
  led[1]=0;
  if(++led[2]>9)
  {
   led[2]=0;
  if(++led[3]>9)
   {
   led[3]=0;
   if(++led[4]>9)
    {
    led[4]=0;
    }
   }
  }
 }
 if(led[4]||led[2]||led[3])
 {
  dis[0]=0;
  dis[5]=3;
  for(i=1;i<5;i++)
  {
   dis[i]=led[i];
  }
 }
}
 
//AT24C16.H*/
#ifndef __AT24C16_H__
#define __AT24C16_H__
#include <intrins.h>
#define uchar unsigned char
#define Delay1us() _nop_();_nop_()
#define Delay2us() _nop_();_nop_();_nop_();_nop_()
#define Delay5us() _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_()
sbit SDA=P3^2; //模拟数据线
sbit SCL=P3^1; //模拟时钟线
//sbit WC=P3^0; //读写控制线
//开始信号
void START_I2C(void)
{
 SCL=1; //空闲状态
 Delay5us();
 SDA=0; // 1->0跳变,开始
 Delay5us();
 SCL=0; //准备发送数据
 Delay1us();
}
//结束信号
void STOP_I2C(void)
{
 SDA=0;
 SCL=1;
 Delay5us();
 SDA=1; // 0->1跳变,结束
 Delay2us();
}
//
//应答信号
void ACK_I2C(bit ack)
{
 SDA=ack?1:0; //发出应答或非应答
 Delay2us();
 SCL=1; //保持一个高电平周期
 Delay5us();
 SCL=0;
 Delay2us();
}
*/
//发送一个字节
bit SEND_BYTE(uchar byte)
{
 uchar i;
 bit ack;
 SDA = 1;
 for(i=0;i<8;i++)
 {
 SDA=((byte<<i)&0x80)?1:0; //赋值发送位
  Delay1us();
  SCL=1; //保持一个高电平周期
  Delay5us();
  SCL=0;
  Delay2us();
 }
 Delay2us();
 SDA=1; //准备接收应答位
 Delay2us();
 SCL=1;
 Delay2us();
 ack=SDA?1:0; //判断应答位
 SCL=0;
 return(ack); //返回应答位
}
//接收一个字节
uchar RECEIVE_BYTE(void)
{
 uchar i;
 uchar byte=0;
 SDA=1; //输入方式,等待接收
 for(i=0;i<8;i++)
 {
  SCL=1;
  Delay2us();
 byte=byte<<1;
  if(SDA) //读数据位,存入byte
  byte+=1;
  Delay5us();
  SCL=0; //提供一个低电平周期
 }
 
 Delay2us();
 return(byte); //返回接收字节
}
//向从机写一个字节
bit WRITE_BYTE(uchar address, uchar byte)
{
 START_I2C();
 if(SEND_BYTE(0xa0))
 return 1; //非应答,返回
 if(SEND_BYTE(address))
 return 1; //非应答,返回
 if(SEND_BYTE(byte))
 return 1; //非应答,返回
 STOP_I2C();
 return 0;
}
//读取从机一个字节
uchar READ_BYTE(uchar address, uchar *byte)
{
 
 START_I2C();
 if(SEND_BYTE(0xa0))
 return 1; //非应答,返回
 if(SEND_BYTE(address))
 return 1; //非应答,返回
 START_I2C();
 if(SEND_BYTE(0xa1))
 return 1; //非应答,返回
 *byte=RECEIVE_BYTE();
// ACK_I2C(0); //应答信号
 STOP_I2C();
 return 0;
}

成品:
3edd751egb4adcc7fae5a.jpg
3edd751egb4adcdacb540.jpg
3edd751eg7877c7e24e6a.jpg
3edd751egb4adcfec40c2.jpg
3edd751egb4add10d031c.jpg
3edd751egb4add1b168ee.jpg
3edd751egb4add29bb6bc.jpg
3edd751egb4add342dd56.jpg
3edd751egb4add3e6350a.jpg
3edd751egb4add48ff9c2.jpg
3edd751egb4add54e9404.jpg
3edd751egb4add5ebbfc0.jpg
3edd751egb4add69732ff.jpg
3edd751egb4add744cfcd.jpg

1、流程
裁判按下复位键,抢答器进入抢答模式,各组人员才可以通过抢答按钮进行抢答,抢答结果显示在抢答器上。

2、实现
使用单片机对输入的电平信号处理(低电平有效):1路为复位,8路为抢答。复位前对8路抢答状态进行判断,防止作弊。一次性对8路抢答器状态进行采样,将结果存储在num数组内,最大可存储4路同时抢答结果。LED显示部分由T0定时器每3ms动态将num数组的结果点亮。蜂鸣器的时长由T0定时器控制

3、原理图
3edd751exb1376dbafc0a.jpg
4、代码

    #include<AT89X51.H>
    #include<stdio.H>
    #include<stdlib.H>
    sfr WDTRST = 0xA6;  
     
    #define OUTPORT1  P0
    #define OUTPORT2  P2
    #define BELL     P3_5
    #define CLR           P3_4
    #define INPORT    P1
     
    Static unsigned char code  digital[]= {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xd8,0x80,0xe1,0x91,0x88,0x86,0xa0,0xa1,0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x58,0x00}; //字库码"0,1,2,3,4,5,6,7,8,j,y,r,e,a,d,0.,1.,2.,3.,4.,5.,6.,7.,8."
    static unsigned char code select[]={0xff,0xfb,0xf7,0xef,0xdf};     //选位字码
     
    static unsigned char  rycle=0x00; //当前数码管显示位控制
    static unsigned char data num[5]={0x4,0x0e,0x09,0x0A,0x09};//检测结果
    static unsigned char data i,j;
    static unsigned char data  count;//蜂鸣器警报时长
    static unsigned char bdata temp;  //8位取样数据
    static bit state=0x00 ;//复位状态保存
    static unsigned int sum[]={0x00,0x00};   //复位键检测时间/自动复位的时长
    static bit timeCLR=0x00;//自动复位状态
    static bit stateCLR=0x00; //首次状态保留
     
    void delay(char t);
    void ready();
     
     
    void init()
    {
          P0 = 0xff;
          P1 = 0xff;
          P2 = 0xff;
          P3 = 0xFF;
          EX0 = 0x00;
     
          EX1 = 0x00;
          ET1 = 0x00;
          ES = 0x00;
          ET2 = 0x00;
          WDTRST = 0x1E;
          WDTRST = 0xE1;
     
          BELL = 0;
     
          TMOD=0x11;
          EA=0x01;
     
          TR0=0x00;
          ET0=0x00;
          TL0=0x18;      //T0=130ms
          TH0=0x02;
     
          count=0;
     
          TL1=0x24;    //T1=4.5ms
          TH1=0xfa;
          TR1=0x01;
          ET1=0x01;
     
    }
    void belltime() interrupt 1
    {
          TL0+=0x18;
          TH0+=0x02;
          if(!count--)
          {
                  BELL=0x00;
                  ET0=0x00;
                  TR0=0x00;
          }
     
     
    }
     
     
    void display() interrupt 3
    {
          TL1+=0x24;
          TH1+=0xFA;
          if(rycle<=num[0])
          {
                 OUTPORT1 = digital[num[rycle]];
                 OUTPORT2 = select[rycle];
                 ++rycle;
          }else  rycle=0x00;
     
     
     
          if(!CLR)
          {
                 sum[0]++;
               if(sum[0]>666&&stateCLR)
                 {
                        timeCLR=!timeCLR;
                      stateCLR=0;
                        state=!state;   //避免按复位键超时,导致状态重复反转
                 }
                 
          }else
          {
                 sum[0]=0;
                 stateCLR=1;                //复位键退出,将状态设置为可复位状态
          }
     
     
          if(timeCLR&&!state)
          {
                 if(sum[1]++>2333)                    //在有抢答结果的情况下,等待复位
                 {
                        if(temp==0xff)                    //防作弊
                        {
                               ready();
                        }else              
                        {    
                               sum[1]=2167-(int)666*rand();        //延迟0.5-2.5S
                        }
                 }
     
          }else sum[1]=0;
     
     
     
     
    }
    void delay(char t)
    {
          count=t;
          TL0=0x18;
          TH0=0x02;
          ET0=0x01;
          TR0=0x01;
    }
     
    void main()
    {
          init();
         while(1){
                 WDTRST=0x1E;
                 WDTRST=0xE1;
                 temp = INPORT;            //对8个连接器同时采样
                 if(!CLR&&temp==0xff)
                 {
                    ready();
                 }
                 for(i=1,j=0;i<=8&&state;i++)         //8个连接器状态检测
                 {
                        if(!(temp & 0x01))
                        {
                               j++;
                               num[0]=j; //num[0]存放首批按下的连接器个数
                               num[j]=(j>1?i+0x0f:i); //存放按下状态的连接器序列
     
                               
                               BELL=0x01;
                               delay(1);
                        }
                        temp >>= 1;
                 }
     
     
                 if(j>0) state=0; //判断是否抢答成功,成功就禁止再次抢答
          
     
     
          }
    }
     
     
    void ready()
    {
          num[0]=0x04;
          num[4]=0x0b;
          num[3]=0x0c;
          num[2]=0x0e;
          num[1]=0x0a;
     
          state=0x01;
     
          BELL=0x01;
          delay(1);
    }

5、成品效果
3edd751exb1376cc6fa63&690.jpg
3edd751exb137729e5330&690.jpg
3edd751exb13777447693&690.jpg
3edd751exb1377d3a5419.jpg

月底CO开关就要送样,上个月底答应小郑要把非独立的检测方式改为独立的方式,总不能食言。利用周末两天时间在家搞这个电路:12个微动开关(24根线)、12组LED,24+12超出89S51总的32个IO口,故增加一片74LS138进行IO扩展。通过对IO口动态扫描电平状态,检测其相对应的微动开关的状态,再将检测的结果写到相对应的LED,LED使用ULN2803进行扩流,弥补单片机驱动能力不足的弱点。

原理图
3edd751ex9e586829ee35.jpg
焊接完成的PCB板
3edd751exa6ec5bcc6206.jpg
3edd751exa6ec5f8db1da.jpg
由于只是扫描和写出,故程序没啥难度,这也是我写的最快的一个程序

#include "AT89X51.H"
sfr WDTRST = 0xA6;  
static unsigned char code write_data[12]={0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,0xfe,0xfd,0xf9,0xf7};
unsigned char i;


void init()
{
 P0  = 0x00;
 P1  = 0xff;
 P2  = 0xff;
 P3  = 0x0f;
 EX0  = 0x00;
 ET0  = 0x00;
 EX1  = 0x00;
 ET1  = 0x00;
 ES   = 0x00;
 ET2  = 0x00; 
 WDTRST = 0x1E;
 WDTRST = 0xE1;

}

void main()
{
 init();
  while(1){
  WDTRST=0x1E;
  WDTRST=0xE1;
   for (i=0;i<12;i++)
  {
   P2=write_data[i] ;
   switch(i)
   { 
    case 0: P0_0=!P1_0; break;
    case 1: P0_1=!P1_1; break;
    case 2: P0_2=!P1_2; break;
    case 3: P0_3=!P1_3; break;
    case 4: P0_4=!P1_4; break;
    case 5: P0_5=!P1_5; break;
    case 6: P0_6=!P1_6; break;
    case 7: P0_7=!P1_7; break;
    case 8: P3_4=!P3_0; break;
    case 9: P3_5=!P3_1; break;
    case 10: P3_6=!P3_2; break;
    case 11: P3_7=!P3_3; break;
    default: break;

   }

  }

 }
}

把程序编译成HEX格式,烧录到89S51中即可,有了躯干和灵魂的PCB板就跑了起来。

组装到箱子里,替换原电路板
3edd751exa6ec4b2b2794.jpg
密麻麻的线,其实很多都是相同信号线(不想再去大费周折的精简重焊,就按原本的电气特性,重新做的这个PCB兼容电路)
3edd751exa6ec4d31f9d2.jpg
3edd751exa6ec4f75c221.jpg
3edd751exa6ec53dee883.jpg
3edd751exa6ec573582b5.jpg
原电路板为立林做的,就留着做应急备用
3edd751exa6eceefa87b7.jpg