|
发表于 2015-1-10 16:03:54
|
mykuangtw 发表于 2015-1-10 15:23 
我觉得你直接贴出来更便于大家讨论学习,只是建议,你决定。
我替他贴出来吧:
一,LCD1602显示部分
作者用一个lcd_B(char i,uchar c,char t),改变参数,反复调用,实现了初始化,清屏,隐藏光标,设置DDRAM地址,字符,字串输出浮点数定宽输出等许多功能。由于代码复用,使得程序简短,高效。这是我们应该学习的。
这部分的浮点定宽输出有一点难度。设有一个浮点数a,我们要显示其中前w个数字,其中保留的小数位数是n。在函数lcd_putf()里,第一个for()循环n次,每次把a扩大10倍(a*=10,先把
a乘以10,再赋给a),n次后,a的要保留的小数部分就成为整数部分,不要的部分仍然是小数。
第二个for()循环w-1次,每次把c(初值=1)乘以10,如此一次,C=10,两次,C=100,三次,C=1000,显然c的0的个数跟要显示的数字宽度w-1相等。
在第三个for(i=0;i<w;i++){ }循环中,字符变量g=b/c;这里的长整型变量b取得的是a的绝对值的整数部分,因为在前面已经有过符号判别:若a>=0,b=a;否则b=-a.而b/c,就得到了当前b的最高位数字。为什么?因为经过第一个for()循环后,a的整数部分已等于要显示的字宽w,假设w=5,则c=10000,C语言里整数相除,结果是商的整数部分,所以当前b的最高位就被存到g里了。整除时,余下的部分为b-g*c,把这部分再给b,进入第二次循环,再取出现在b的最高一位,如此循环w次,要显示的数字就由高位到低位依次由g得到。每取出一位到g,就把c除以10,即c/=10;这是因为每次取过b的最高位后,余下的b-g*c位数已经少了一位,取现在的b的最高位,不能再整除当初的c,而要除以小了10倍的c.
g+=48;是什么意思?虽然声明g是字符型变量,但它从b/c得到的是整数,是数字。1602显示的是“字符”,而不是“数字”。查1602的字符代码表可以发现,数字0--9对应的代码是30H--39H,也就是说,数字必须加上十进制数48才能成为对应的字符,在1602屏上显示。如果g<48,或者g>48+9,就说明g不是0--9之一的数字,那就显示一个*号。
个位的0要显示,但个位前面的第一次出现的0不要显示,而显示一个空格,怎样实现的呢?看if(g==48&&i<w-n&&!fi) g=' ';可见要同时满足3个条件,才显示一个空格。第一,g==48,表示待显示的这一位数的字符代码是48,就是数字0;第二,当前要显示的是个位数前面的字符。w是要显示的数字位数,例如假设w=5,一共要显示5个数字,(不包括小数点,但包括空格),其中要保留的小数位数n=2那么显然w-n=3就是整数的位数。如果i从1起循环,那么i=w-n对应的那位g就是个位数字。但是该for()循环中,i从0开始,所以i=w-n-1对应的那位g才是个位数字。因此,条件i<w-n-1;表示当时取得的g是个位数之前的一个数字。第三个条件!fi 这里的fi初值为0,显示过非空格后,才有fi++,所以只有当fi==0,即还没有显示过非空格,!fi才会是1。由此可见,数字0.23, 104.1, 0.000,其中的0都是要显示的,而01.25中的0要代之以空格,即 1.25
因为当i==w-n时,对应要显示的是十分位的数字g,因此,在显示十分位的g前,先显示一个小数点'.' 。最后,如果没有小数部分,即n==0,就补显示一个空格。
------------------------------------------------------------------------------
二,频率计数部分
根据LC回路的谐振频率来计算L或C,必须测出频率。
程序里用TMOD=0X51设置T1为16位计数器,T0为16位定时器。由p3.5从LM393的7脚得到计数脉冲
。16位计数器溢出,发生T1中断时,说明输入的脉冲数是2的16次方个,用初值为0的fa++对溢出次数计数,再用a=fa,b=TH1,C=TL1,存放溢出次数,及读取计数值时16位计数器里还剩的值。显然,计数值应该是a乘以2的16次方,就是*256*256,加b里的数是TH1里的,每一个都是低8位计满256进位来的,所以要加上b*256,至于C,它是TL1里的,有一个就算一个,因此再加上C,所以总的计数值为:
a*256L*256L+b*256L+c, (数字256尾的L表示指定它为长整型)
这个数值是闸门打开到关闭这段时间里计得的,因此必须要计算一下闸门开了多长时间?电路里晶振是18.432MHZ,单片机STC12C5A60S2是1T的,即晶振的一个周期作为一个机器周期,就是18.432分之一微秒,程序里用AUXR|=0x40设置了T0的T0X12位为0,即每12个机器周期触发定时器T0一次,至于T1,因为计数脉冲来自单片机外部,所以是每来一个脉冲计数器加1的。
当条件if(++Tk>=Tkm)为真时,T0中断才读取T1的计数值,所以在Tkm=24的条件下,TK已经是24,即T0中断发生24次时读取T1计数值.注意++TK,是先让TK自增1,后判断它是否>=Tkm,所以即使不执行读取T1计数值,每次T0中断发生,TK还是要自增1的.因此,读取计数值时,Tkm的值就等于T0中断发生的次数,即闸门时间为Tkm*12*1/18.432再乘以2^16再乘以10^-6秒。若Tkm=24,算出其值是1.024秒,所以程序里的feq是1.024秒内的脉冲计数值,因此,后面的电感计算程序calcl( )里要先用f=feq/1.024;把闸门1.024秒的计数值变为每秒的计数值,即频率单位HZ。如果频率较高,(计数值feq>=2500),闸门时间就取短些。当Tkm=12时,由于T0中断12次就读取计数值,即使频率不变,闸门时间变为原来的1/2,计数值也必然是原来的1/2,为使后面f值不变,必须把1/2闸门时间内的计数值乘以2,这就是程序里*(24/Tkm)的作用。
后面还有不少值得研究之处,如:在印刷板上P1.6是引出作channel(6)的,为何电路图里是P3.6,而P1.6脚却是空着的?密勒效应改正系数1/(1-Mr*Rf*f)的来由?测量二极管的Rd时用的dv=5/10.24/7.25,其中7.25是LM393的放大倍数,10.24是什么?测电池电压的电路在哪里?Vbat*0.00146,其中的0.00146的来源是什么?等等......由于我自己水平低,十分希望对程序感兴趣的朋友们来谈谈见解,使矿坛的研究气氛更加浓厚!
|
|