矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
查看: 9445|回复: 60

我对LC/Rd表程序的学习体会点滴

[复制链接]
     
发表于 2015-1-9 14:48:07 | |阅读模式
    XJW01老师制作了几种单片机控制的1602屏显示的测量仪表,不但有制作指导,而且公开了源程序,难怪坛里有人称他是位“大好人”。对于我们这些单片机入门级的“菜鸟”,这位高手,好人的制作资料和程序,真是不可多得的“宝贝”。你看,2012年的帖子,到现在还有不少人关注,这就充分证明好帖子的生命力是永久的!
    我跟单纯的“焊机派”相比,更加关注单片机的程序。我想,只有认真学习人家成功的程序,弄懂每一句的道理,编程水平才会有长进。可惜的是自己太“菜”了,尽管程序里XJW01老师加了不少的注释,我还是有许多不明白之处。因此,又将几年前的帖子打开仔细研读,居然也多少有点领悟!特地写出来,请大家指点。更希望此举起“抛砖引玉”的作用,使矿坛的电脑与单片机栏目更加红火!附我的学习体会点滴如下: LCRD表程序学习理解点滴.txt (4.41 KB, 下载次数: 555)

补充内容 (2015-1-12 13:01):
仔细研读程序,发现测量电池电压的channel(6)确实应该是单片机的P1.6,但是翻遍该版40页,所有电路图中STC12C5A60S2的第7脚即P1.6都是空着的!只有一个网友说了句:电路图里漏画了100K和51K电阻,是电解电容附近的.

补充内容 (2015-1-12 13:06):
于是我又一次翻遍40页,终于找到一块印刷板图,上面清楚地表明: P1.6脚经铜箔接在100K和51K电阻的交点上!啊,这才是测量Vbat的模拟量的取样点,疑点终于消除了!
发表于 2015-1-10 15:23:26 |
我觉得你直接贴出来更便于大家讨论学习,只是建议,你决定。
     
发表于 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的来源是什么?等等......由于我自己水平低,十分希望对程序感兴趣的朋友们来谈谈见解,使矿坛的研究气氛更加浓厚!
     
发表于 2015-1-10 19:36:21 |
lxa000 发表于 2015-1-10 16:03
我替他贴出来吧:

一,LCD1602显示部分

你会改程序加程序不?我有个4730的收音机,没有显示频率功能,你能不能帮我加上,也能验证你的学的单片机怎么样
     
发表于 2015-1-11 02:56:06 |
4730的收音机的资料有吗?
     
 楼主| 发表于 2015-1-11 08:36:42 |
lxa000 发表于 2015-1-10 16:03
我替他贴出来吧:

一,LCD1602显示部分

    谢谢你帮我贴了出来!由于用零碎时间离线写,怕断断续续的阅读不方便,就用附件了。但有的网友下载不了,反而影响浏览了。以后还是写一点发一点吧。
     
 楼主| 发表于 2015-1-11 09:30:00 |
lxa000 发表于 2015-1-10 16:03
我替他贴出来吧:

一,LCD1602显示部分

    测量二极管的RD是XJW01老师的LCRD表的亮点。但程序里一句dv=5/10.24/7.25;就令我傻眼了。仔细研究后,发现要弄明白三点:

一,注释说是转为毫伏,但式中的5是电源电压5V,作参考电压的,为什么变成毫伏了?
二,对于10位AD转换,求模拟量要将转换结果乘以因子(参考电压/1024),分母是1024,为什么变成10.24?
三,为什么要除以7.25?

    经过查阅资料,得知:10位AD转换器,用5V参考电压时,若输入模拟量Vin=2.5V,转换结果是D7=1,其余位为零,即得到二进制数的转换结果是1000 0000,或十进制数512,而不是2.5.由此得到公式:模拟量Vin=AD转换结果乘以因子5/1024

   又查XJW01老师的程序(注:原发表在本坛的矿石收音机板块,2012年,有40页,名叫"大小通吃的电感表,精度还很好")发现:他在函数
getADC10( )里,循环采样100次,然后除以10,四舍五入取整:c=(c+5)/10,如此返回的C就是转换值的10倍,即getAD10( )=10*转换结果,因此  10*模拟量Vin=getAD10( )*5/1024,两边同乘以100,得:
   1000*模拟量Vin=100getAD10( )*5/1024=getAD10( )*5/10.24 式左边的模拟量单位本来是V,乘了1000,就变成毫伏了.

   最后,7.25的来源:分析电路图,电压比较器1/2 LM393是接成7.25倍的负反馈放大器的(略去LM393的输入电流,V0/Vi=(Rf1+Rf2)/Rf1=116k/16k=7.25).程序里的模拟量是用P1.7接在LM393的输出脚1脚测到的,是被放大了7.25倍的,我们要求的是输入端加在二极管上的电压,因此又要将上式结果除以7.25,最终得到:

                            dv=5/10.24/7.25     这个dv并非什么电压的微分,而是:从getAD10( )取得的值变为模拟量mV时都必须乘的一个因子.
     
 楼主| 发表于 2015-1-11 11:40:30 |
江边树 发表于 2015-1-11 09:30
测量二极管的RD是XJW01老师的LCRD表的亮点。但程序里一句dv=5/10.24/7.25;就令我傻眼了。仔细研究后, ...


纠正:10位AD转换器,当模拟量Vin=2.5V,而参考电压取自单片机电源电压Vcc=5V时,由逐次比较逼近的原理可知:D9=1,而其余各位均为零,即二进制数10 0000 0000,或十进制数512,而不是2.5......
     
 楼主| 发表于 2015-1-12 10:05:07 |
对8#说法的纠正已经贴出:并非所有从getAD10( )取得值的都要乘以因子dv......
     
 楼主| 发表于 2015-1-13 11:49:24 |
江边树 发表于 2015-1-12 10:05
对8#说法的纠正已经贴出:并非所有从getAD10( )取得值的都要乘以因子dv......

    一般的资料认为,锗二极管导通电压0.2V,硅二极管导通电压0.6V,低于这个电压二极管是不通电的,称之为"死区电压".但是,玩过矿石收音机的朋友都知道,在离电台较远,天线也不太长的情况下,二极管或矿石上所加的电压不过是几十到100多毫伏,还在所谓的"死区"呢,但是耳机里却有声音了.耳机的灵敏度高,阻抗又匹配得好的话,几个毫伏也能出声的.这说明二极管不存在死区.这就是XJW01老师程序里的测量二极管的Rd成为亮点的原因.我最初注意这个LC/RD表,就是被所谓的RD吸引的!
    拿万用表测RD是很麻烦的,要搞一个电路,给二极管提供大小不同的两个电流,然后根据dv/di算出零点附近二极管表现出来的动态电阻.我第一次看到程序里的贴图很惊讶,二极管的RD居然会大到几十K,几十兆,几百兆!难怪有"死区"一说.电阻虽大却绝对不是无穷大,这就是RD的吸引人之处.
    程序里的P3.6是做什么的,我一开始不明白,也没有什么详细解释.所以根据我对RD的认识,还想在电路图和电路板上找转换开关,结果没找到.那么,怎样改变加给二极管的电流呢?最后仔细研究了STC12C5A60S2的资料,发现它跟一般的51单片机几个不同之处之一是有I/O模式设置功能.程序里的P3M1|=0X40,即二进制数0100 0000 ,因为P3M1是SFR,即特殊功能寄存器,它的复位值是0000 0000
所以跟0X40按位或运算后再赋给P3M1,它就是0X40了.同理,程序里P3M0 &=~0X40,则P3M0就是0000 0000 .它们从低位到高位对应着P3的P3.0---P3.7,可见P3.6的M1=1,P3.6的M0=0,查STC12C5AS2的资料可知这是把P3.6设置成"高阻口".这就是说,仿佛P3.6从电路里断开了.显然,这时只有5V电源经过大电阻R8=1MΩ对二极管供电,是很小的电流,很小的偏压V1;
  当P3M0 |=0X403M1 &=~0X40;可知P3.6的M1=0,M0=1,查资料表可知这时P3.6被设置成"推挽强上拉输出",由STC12C5AS2的I/O口结构可知,这时P3.6使用的推挽管输出,且上管导通,等效直接通+5V,最大可提供20mA的输出电流.从电路里看,P3.6把R7=51K上端接到+5V,就是跟R8并联一起向二极管提供较大的电流,较大些的偏压V2.
    程序的巧妙之处在于不用机械的转换开关就能对二极管施加大小不同的两个电压电流,从而为计算RD提供了数据V1,V2.接下来计算RD还有难点,下次再说了.
     
发表于 2015-1-13 13:21:57 |
学习了,到现在我的表还没焊完呢,哈哈
     
发表于 2015-1-13 13:26:19 |
谢谢楼主收藏待用
     
发表于 2015-1-13 15:06:37 |
江边树 发表于 2015-1-11 11:40
纠正:10位AD转换器,当模拟量Vin=2.5V,而参考电压取自单片机电源电压Vcc=5V时,由逐次比较逼近的原理可 ...

在纠正你下,  10位AD,  AD值从 0-1023  共1024个。

应该除以1023

所以公式应该是  ,   AD= ad_val *基准参考电压 / (2^位数   -1)

对应你的帖子的话 就是  AD=ad_val  *5   /1023  

放大1000倍的话就是  AD=ad_val  *5   /1.023  

放大100倍的话就是 AD=ad_val  *5   /10.23


其实习惯上我都是放大1000倍。
发表于 2015-1-13 15:14:03 |
STC15F2K60S2还好用一些
     
发表于 2015-1-13 15:19:04 |
举个例子,  基准电压使用5V  ,采集电压为0V的时候 ,ad_val =0,  采集电压为5V的时候ad_val=1023

采集电压为1V的时候  ,1 / 5   =ad_val / 1023   ,得到 ad_val= 204    ,
采集值  AD=ad_val  *5   /1.023  = 997   ,也就是  0.997V ,放大1000倍的目的是 变成 整型减少浮点型变量。

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2025-4-29 22:15

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表