HSIAO-YANG CHEN 发布的文章

经过N次的软硬件调试,终于是完美了。
过程中遇到的问题:①信号的误动作②程序跑飞的硬件复位③光耦电路的电阻选取④连续重复测试过程,程序的健壮性
3edd751exa454f18949f5.jpg
3edd751exa454f714443c.jpg
实物图
3edd751exa454f76092f2.jpg
原理图,Port1.0和Port1.1串接到主微动,Port2.0和Port2.1直接连接辅微动。当主微动导通时,由于电阻分压使得光耦导通,P3.3为低电平;辅微动闭合,P3.2为低电平。主微动信号和辅微动信号为互斥的两个信号量,当主微动为闭合状态,辅微动为断开状态,反之亦然。

#include "AT892051.H"
 
#define uint  unsigned int
#define Master_switch P3_3
#define Slave_switch  P3_2
#define Yellowlight   P3_0
#define Redlight     P3_1
#define Bell         P3_7
#define rst         P1_7
#define delaytime    20 //20*50ms
#define watchtime    40 //40*50ms
#define count     6  //6*50ms
#define chknum     48 //48*20us

static uint dtime;
static uint wait;
static uint dstate;
static uint dcheck;
static uint belltime;



bit checkstate(bit p)   //周期(21+chknum*20)us
{
 int sum[]={chknum,chknum};
 if(p)        //检测主微动为0,从微动为1
 {
  while(sum[0]&&sum[1])  // chknum*20us
   {
   Slave_switch=1;
   if(Slave_switch)
   {
    sum[0]--;
   }
   else
   {
    sum[1]--;
   }
   }
 }
 else
 {
  while(sum[0]&&sum[1])
   {
   Master_switch=1;
   if(Master_switch)
   {
    sum[0]--;
   }
   else
   {
    sum[1]--;
   }
   }
 }
  return  (bit)sum[0];
}

//看门狗T0监测主程序
void watchdog() interrupt 1  using 3
{
 
 if(!--dtime)
 {
   rst=0;     //主程序跑飞,系统复位
  }
 if(!--belltime)
 {
  Bell=1;
 }

}

//看门狗T1检测看门狗T0
void int50ms() interrupt 3  using 1
{
 if(wait) wait--;
 if(dcheck==dtime)  
 {
  if(++dstate>watchtime) rst=0;   //看门狗T0停止,系统复位
 }
 else
 {
  dcheck=dtime;
  dstate=0;
 }
}

//初始化
void init()
{
 EA = 1;
 ET1 = 1;
 ET0 = 1;
 PT1 = 0;
 PT0 = 0;
 TMOD = 0X11;
 TL1 = 0X00;
 TH1 = 0X00;  
 TL0 = 0XFF;
 TH0 = 0X00;
 TR1 = 1;
 TR0 = 1;
 P3 = 0xff;
 P1 = 0xff;
 dstate = 0;
 dcheck = 0;
 Bell = 0;
 belltime=5;
}

//指示灯状态
void LED()
{
 if(checkstate(1)) Redlight=0;
 else Redlight=1;
}

void main() using 0
{
 init();
 for(dtime=watchtime;1;dtime=watchtime)
 {
  LED();

  
  if(checkstate(0))
  {
   Yellowlight=0;
   wait=delaytime;  //在规定时间内,检测从微动断开
   while(wait)
   { 
    if(checkstate(1))
    {
     Redlight=0;
    }
    else
    {
     Redlight=1;
     wait=delaytime;
     break;
    }
   }
   if(!wait)     //超时,触发蜂鸣器
   {
    Bell=0;
    belltime=20;
    continue;
   }


     
   for(dtime=watchtime;checkstate(0);dtime=watchtime)
   {
    if(checkstate(1)) //若从微动闭合,则在规定时间段内检测主微动为断开状态
    { 
     Redlight=0;
     wait=delaytime;
     while(wait)
     {
      if(!checkstate(0))
      {
       Yellowlight=1;
       wait=delaytime;
       break;
      }
      LED();
     }
     if(!wait)   //超时,触发蜂鸣器
     {
      Bell=0;
      belltime=20;
     }

    }else Redlight=1;
    }

  
   
    Yellowlight=1;
   LED();
   wait=delaytime;
   
   while(wait)  //在规定时间内,检测从微动为闭合状态
   {
    if(checkstate(1))  //若从微动为闭合状态 
    {
     Redlight=0;
     wait=count;
     while(wait)   //检测持续时间是否满足count*50ms
     {
      if(!checkstate(1)) //在count*50ms时间内,若从微动断开,则中断退出
      {
       Redlight=1;
       wait=delaytime;
       break;  
     
      }


     }
     if(!wait)  //检测时间满足count*50ms,退出
     {
      wait=delaytime;
      break;
     }
     else   //未能持续count*50ms,在规定时间段内检测主微动为闭合状态
     {
      while(wait)
      {
       if(checkstate(0))
       {
        Yellowlight=0;
        wait=delaytime;
        break;
       }       
      }
     }

    }
    else  //检测不到从微动闭合的信号量,但在规定时间段内检测到主微动再次为闭合状态,中断退出
    {
     Redlight=1;
     if(checkstate(0))
     {
      Yellowlight=0;
      wait=delaytime;
      break;
     }
    }
   }
   if(!wait)   //超时,触发蜂鸣器
   {
    Bell=0;
    belltime=20;
   }
  }else
   Yellowlight=1;
 }
}

主控程序,看门狗T0检测主程序,看门狗T1检测看门狗T0,看门狗T1又作为主辅微动切换时间的定时器,当程序跑飞,触发硬件复位。主辅微动切换最长时间为1秒,超时触发蜂鸣器。

这是我的第一个51项目(很久没写过程序了,顺便活动下大脑,不然要生锈了)。
原理:变化的电场产生变化的磁场,变化的磁场切割线圈产生感应电流。经过全波整流,直接将电平信号送入AT89C2051,利用芯片自带的比较器(P1.0、P1.1)和存放比较结果P3.6。通过轮询P3.6的状态,启动或停止计时中断,每次计时清空计时器,计时时间小于0.1S不显示结果(屏蔽了部分由于干扰而产生的误动作)
电路设计:
3edd751ex9e58693c974a.jpg
焊接完成的电路板:
3edd751ex9e5868ff9385.jpg
51汇编代码:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; AT89C2051通断计时程序 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 定时器T0溢出周期为10MS,P1.0(同相端)、P1.1(反相端)作为电压比较器
; P3.6为比较器的输出结果,P1.2-P1.7和P3.0、P3.1口(A-DP)为字符输出口,采用共阳显示管。
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 中断入口程序 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
ORG 0000H ;程序执行开始地址
LJMP MAIN ;跳到标号MAIN执行
ORG 0003H ;外中断0中断程序入口
RETI ;外中断0中断返回
ORG 000BH ;定时器T0中断程序入口
LJMP INTT0 ;跳至INTTO执行
ORG 0013H ;外中断1中断程序入口
RETI ;外中断1中断返回
ORG 001BH ;定时器T1中断程序入口
RETI ;定时器T1中断返回
ORG 0023H ;串行中断程序入口地址
RETI ;串行中断程序返回
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 主 程 序 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
MAIN:

MOV R0,#70H ;清70H-77H共8个内存单元
MOV R7,#08H ;
CLEARDISP: MOV @R0,#00H ;
INC R0 ;
DJNZ R7,CLEARDISP ;
MOV 70H,#07H;初始化信息显示
MOV 71H,#06H
MOV 72H,#0AH
MOV 73H,#0BH

MOV TMOD,#01H ;设T0为16位定时器
CLR C ;清进位标志

MAIN1:
LCALL DISPLAY ;调用显示子程序
LCALL DS8MS;调用延迟减少误动作
JB P3.6,START ;P3.6口为1时开启定时器

JC STOP;据进位标志位判断是否关停T0
SJMP MAIN1
STOP:
CLR C;清除标志位
CLR TR0 ;关闭T0定时器
CLR ET0 ;关闭T0中断
CLR EA ;关闭总中断
SJMP MAIN1

START:
JC MAIN1;据标志位情况判断是否需清零重新开启T0
SETB C
MOV R0,#75H
ACALL CLR0 ;计时单元清0
MOV R0,#77H
ACALL CLR0 ;计时单元清0
MOV TL0,#0f0H ;10MS定时初值(T0计时用)
MOV TH0,#0D8H ;10MS定时初值
SETB EA ;总中断开放
SETB ET0 ;允许T0中断
SETB TR0 ;开启T0定时器
SJMP MAIN1


;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 10毫秒计时程序 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;T0中断服务程序
INTT0: PUSH ACC ;累加器入栈保护
PUSH PSW ;状态字入栈保护
CLR ET0 ;关T0中断允许
CLR TR0 ;关闭定时器T0
MOV A,#0F7H ;中断响应时间同步修正
ADD A,TL0 ;低8位初值修正
MOV TL0,A ;重装初值(低8位修正值)
MOV A,#0d8H ;中断响应时间同步修正
ADDC A,TH0 ;高8位初值修正
MOV TH0,A ;重装初值(高8位修正值)
SETB TR0 ;开启定时器T0
MOV R0,#75H ;指向毫秒计时单元(74H-75H)
ACALL ADD1 ;调用加1程序
CLR C ;清进位标志
CJNE R4,#9Ah,ADDMM ;判断是否等于100毫秒
ADDMM: JC OUTT0 ;小于100毫秒时中断退出
ACALL CLR0 ;大于或等于100毫秒时对毫秒计时单元清0
MOV R0,#77H ;指向秒计时单元(76H-77H)
ACALL ADD1 ;秒计时单元加1秒
CLR C ;清进位标志
CJNE R4,#9Ah,ADDSS ;
ADDSS: JC OUTT0 ;小于100秒时中断退出
ACALL CLR0 ;大于或等于100秒计时单元清0
OUTT0:
MOV R0,#75H
MOV A,@R0
MOV R3,#02H
FOR:INC R0
ORL A,@R0
DJNZ R3,FOR ;小于0.1秒不做输出显示
JZ DIS
MOV 70H,74H
MOV 71H,75H
MOV 72H,76H
MOV 73H,77H
DIS:
POP PSW ;恢复状态字(出栈)
POP ACC ;恢复累加器
SETB ET0 ;开放T0中断
RETI ;中断返回

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 加1子程序 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
ADD1: MOV A,@R0 ;取当前计时单元数据到A
DEC R0 ;指向前一地址
SWAP A ;A中数据高四位与低四位交换
ORL A,@R0 ;前一地址中数据放入A中低四位
ADD A,#01H ;A加1操作
MOV R4,A;保留用于判断是否进位
DA A ;十进制调整
MOV R3,A ;移入R3寄存器
ANL A,#0FH ;高四位变0
MOV @R0,A ;放回前一地址单元
MOV A,R3 ;取回R3中暂存数据
INC R0 ;指向当前地址单元
SWAP A ;A中数据高四位与低四位交换
ANL A,#0FH ;高四位变0
MOV @R0,A ;数据放入当削地址单元中
RET ;子程序返回
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 清零程序 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;对计时单元复零用
CLR0: CLR A ;清累加器
MOV @R0,A ;清当前地址单元
DEC R0 ;指向前一地址
MOV @R0,A ;前一地址单元清0
RET ;子程序返回

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 显示程序(70个机器周期+4*20*25*2=4070us) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 显示数据在70H-73H单元内,用4位LED共阳数码管显示,P1.2-P1.7和P3.0、P3.1口(A-DP)输出段码数据,P3.3-P3.5、P3.7口(10ms|100ms|1S|10S)作
; 扫描控制,每个LED数码管亮1MS时间再逐位循环。
DISPLAY: MOV R1,#70H ;指向显示数据首址

;P3.3扫描显示
MOV A,@R1 ;取显示数据到A
MOV DPTR,#TABP3;取段码表地址
MOVC A,@A+DPTR ;查显示数据对应段码
ANL A,#0F7H;显示端口
MOV P3,A;段码和端口放入P3口
MOV A,@R1 ;取显示数据到A
MOV DPTR,#TABP1 ;取段码表地址
MOVC A,@A+DPTR
MOV P1,A ;段码放入P1口
LCALL DL1MS ;显示1MS
INC R1 ;指向下一地址
;P3.4扫描显示
MOV A,@R1
MOV DPTR,#TABP3
MOVC A,@A+DPTR
ANL A,#0EFH
MOV P3,A
MOV A,@R1
MOV DPTR,#TABP1
MOVC A,@A+DPTR
MOV P1,A
LCALL DL1MS
INC R1
;P3.5扫描显示
MOV A,@R1
MOV DPTR,#TABP3
MOVC A,@A+DPTR
ANL A,#0DEH
MOV P3,A
MOV A,@R1
MOV DPTR,#TABP1
MOVC A,@A+DPTR
MOV P1,A
LCALL DL1MS
INC R1
;P3.7扫描显示
MOV A,@R1
MOV DPTR,#TABP3
MOVC A,@A+DPTR
ANL A,#07FH
MOV P3,A
MOV A,@R1
MOV DPTR,#TABP1
MOVC A,@A+DPTR
MOV P1,A
LCALL DL1MS

MOV P3,#0FFH  ;一次显示结束,P3口复位
MOV P1,#0FFH ;P1口复位
RET ;子程序返回
TABP3:  DB 0FFH,0FFH,0FDH,0FDH,0FDH,0FDH,0FDH,0FFH,0FDH,0FDH,0FDH,0FDH
TABP1:  DB 003H,0E7H,093H,0C3H,067H,04BH,00BH,0E3H,003H,043H,047H,00FH
;共阳段码表 "0"  "1"  "2" "3"  "4"  "5" "6"  "7"  "8" "9"  "y"  "b"
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 延时程序 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;1MS延时程序,LED显示程序用
DL1MS: MOV R6,#14H
DL1: MOV R7,#19H
DL2: DJNZ R7,DL2
DJNZ R6,DL1
RET
;8MS延时程序
DS8MS: ACALL DISPLAY
ACALL DISPLAY
RET

;
END ;程序结束

将以上代码编译链接成.HEX文件,烧入AT89C2051,上机
通电初始化信息:“BY 67”
3edd751ex9e586829ee35.jpg
测试通电时间(显示范围0.1S-99.99S):
3edd751ex9e5867544eb5.jpg
感觉很HAPPY,一直在前进,学无止尽。。。。

这个R1的选择跟输入有关,就比如输入最大12V,输出5V,要使7805不加散热片,那7805上的功率就要小于1W
7805上最大通过的电流要小于 1W/(12-5)V≈0.143A
取I=0.1A 假设Q1的发射极电压为0.7V 那这个R1的取值为 0.7V/0.1A=7Ω (注意电阻的功率)(实际使用时如果7805还是太热可以再加大R1,减小7805通过的最大电流)

Q1的作用是当输入电流小于0.1A时,Q1截止(Vbe<0.1A*7Ω),也就是<100mA的电流全部同7805通过,
当电流大于100mA时(Vbe>0.1A*7Ω),7805还是通过100mA电流,剩下的电流由Q1全包了。
3edd751ex91b5d4a50e46.png
转载于 http://www.ourdev.cn/bbs/bbs_content_all.jsp?bbs_sn=3424851

前些日子,哥笔记本声卡的右声道挂了,于是便拆开分析了一番,大概推测是ALC883芯片损坏了,实在是太杯具了,默哀小螃蟹。

由于损坏的是笔记本声卡模块,一般很难买到合适的替换之,上淘宝搜索了一番,竟然也有拆机单卖的声卡模块的,于是便下手淘了一块。

拆换下来的这一块声卡模块,哥也琢磨着怎么继续玩。经过GOOGLE,搜索到了不少好东东,很多DIY爱好者都已经走在哥的前面啦,都在玩芯片升级
3edd751ex916848006183.jpg3edd751ex916847e1e79e.jpg
通过代换相同引脚功能更高版本的CODE,来提升音质,ALC883属于低端的集成声卡,而ALC888属于中高端的集成声卡,通过一些评论侧面了解到ALC888的音质,于是决定动手试试。

看着这么小块集成声卡芯片,哥迷茫啦,怎么下手呢?整个芯片的面积都不到一个指甲的大小,加之工具实在太有限了,家中就只有一把普通的电烙铁,根本无法完成这种超高难度的焊接,对于焊接贴片,烙铁的温度也是非常关键,温度高了,芯片非常容易坏。于是又得升级装备---恒温焊台。买东西就上淘宝(非广告),看价格合适就下手。于是乎买了不少东西,主要用到工具如下
3edd751ex9166d77338a1.jpg
主角恒温焊台,白光非原装,号称是进口的1321芯,加热效果还不错
3edd751ex9166d7826eb2.jpg
焊完的板子都是焊油,所以需要清洗下
3edd751ex9166d7a0ef71.jpg
装洗板液太好用啦,又有毛刷
3edd751ex9166d7b04acb.jpg
看似多此一举,既然买了就用下,确实看得非常清楚
3edd751ex9166d7cec7f3.jpg
这种焊蜜,焊接的时候一点烟都没有,果然是焊接BGA的必备品
3edd751ex9166d7ddf8a1.jpg
后来卖家电话建议哥买0.5mm的,0.3mm估计太细啦!
3edd751ex91684e72f0ac.jpg
把芯片拆下来了,最好用此吸锡线把焊点清理下
3edd751ex9166d7ed46a9.jpg
这个就是ALC888芯片,大小6mm*6mm

  为了确保把损坏的ALC883取下,且不破坏板子,只能拿小美工刀,将芯片的脚小心翼翼的切下,再用焊台把留在板上的脚焊掉,最后用吸锡线把板上的焊点清理平整,最好再用洗板液清洗下,就像新的一样。接下来就是焊接了,把芯片和板子对上脚,先用烙铁把对角的管脚焊住,在管脚适当加一些焊蜜,烙铁粘住一些焊锡,板子稍倾斜,开始拖焊,先把四个边的管脚都托一遍,再来慢慢拖焊,遇到比较大的焊点,可以借助吸锡线,吸走一部分焊锡,同一边不要拖焊时间过长,基本上托三遍就搞定啦,接下来就是清洗工作了!

  把焊好的声卡模块,安装进机子里。插上电源开机,进入系统没声音,但有提示找到新硬件,打开设备管理器,可以很清楚的看到是ALC888硬件ID,证明此硬件已经被识别。重装完驱动,内置的扬声器还是无法正常输出声音,外接音箱完全OK,音频配置程序相比原机多了很多声道的控制,但是笔记本就2个插孔哪里能支持7.1的声道。看来光升级音频CODE还是不够的,不能和机子的BIOS完全配合好,怎么升级都白搭。现在的这个新声卡对于BIOS来说是无法识别的,BIOS无法控制它的行为。每1个设备ID都是由1个硬件ID+子扩展ID,硬件ID取决于硬件本身,而子扩展ID可以由BIOS进行扩展,子扩展ID帮助驱动识别声卡的功能需求。现在驱动将其识别为支持7.1声道的设备,这显然是错误的,现在的芯片就只是运行在其全功能的状态下,并未对其进行功能删减配置。既然都分析出问题的出处了,要解决就容易啦,莫非就是修改BIOS,使其正确识别新的硬件ID,原来是0883,现在是0888,只要把0883替换成0888,应该就行啦。由于程序的编码都是由低往高,所以0883对应的HEX:8308,用hiew32打开BIOSCODX.ROM(Phoenix BIOS),查找分析替换,把改完的BIOS重新刷机,哇!这下就完美啦,和原来配置界面的差不多只是多了一些DTS特性
3edd751ex9168552dacfb.jpg
内置的扬声器也正常啦!

AWARD ACPITBL 动态法已经研究透了,换研究AMI动态法,由于AMIBIOS主模块1B模块映射到内存是离散的,所以修改的代码最好不要乱跳转,否则极易出现一些无法预知的情况。

1、还是采用XSDT前移、SLIC随后的方法。由于空间限制,所以只能把SLIC加到RSDT里,RSDT长度增加4,即为0X28+0X4=0X2C;接下来就要修正XSDT相关的操作
3edd751ex8c4a7b83eba1.jpg
2、修正XSDT在 RSD PTR的指针,0X100改为0X3C
3edd751ex8c4a7d6c2d3e.jpg
3、修正FACP(相对RSDT 0X200)在XSDT的地址,0X124改为0X3C+0X24=0X60
3edd751ex8c4a7bdf88ff.jpg
4、修正XSDT校验,0X100改为0X3C
3edd751ex8c4a7c68d5f0.jpg
5、修正XSDT增加子表地址,把0X104改为0X40、0X100改为0X3C
3edd751ex8c4a7ca5cb92.jpg
6、以上的步骤完成了XSDT的修正,接下来就是想方设法把SLIC加到RSDT、XSDT中,可以考虑移除一些表的校验如OEMB,换成手工校验
3edd751ex8c4ac8b89afa.jpg
增加SLIC添加代码,移除OEMB的校验代码
3edd751ex8c4a7a715150.jpg
如果空间允许,甚至可以调用RSDT/XSDT增加子表的方法,直接在RSDT和XDST里增加SLIC引用