作为一名经济学专业毕业又从事宏观经济分析的2B,搞电子就成了业余爱好,自学了一段的C51编程,总看书加只试验书里的例程,自己不去实际做点什么,感觉真的没有什么大长进,像台复印机一样的运行,不舒服。那天忽然想到,可以用c51来给我那丐中丐版的GL8加个自动大灯啊,因为有几次过长隧道忘记开灯,感觉视线还是蛮受影响的, 虽然我非常不喜欢别克原厂的自动大灯,一上电就开灯,总觉得影响电瓶寿命,而且前一段用358做主控做了个,但时延受限于电容参数感觉很不准确,就放弃了,同时那方案的光线采样也没有延续性,完全是实时的机械反馈。所以,开始思考怎么用c51实现我想要的自动大灯:
1、打火前要不启动或者有足够时延给我打火。
2、开灯后不会因为环境光线小变化立即关灯,在达到一定昏暗度以后必须要达到指定的亮度才关灯,这样开车时会相对安全些。
3、自动大灯系统可以随时关闭,在运行中随时执行手动动作优先,即原车的大小灯手动开关功能优先,手动需求要不受影响。
4、对环境光线的采样结果要是持续一段时间(N秒)的,然后统计这一时段内采样结果的暗度与亮度数据占比,按占比结果来决定开灯还是关灯。
5、对原车线束不可以有损害,所有器件安装也不在原车上打孔,要尽量找合适的位置来安置。
6、系统是否工作要有视觉指示,这对于有强迫证倾向的银来说。。。很重要。
好吧,一切慢慢从头开始说。。。记录下来最大的好处就是,以后有问题了一查就可以明白怎么维修了,毕竟自制的东东,木有说明 书的,这对于记忆长期处于混乱态的自己来说,异常重要。。。。
中控台前部长期看不舒服的两个空孔位。。。
查到资料,原来一个是环境光线传感器位置,另一个阳光照度传感器位置,嗯嗯,分析个,应该是一个管自动灯的信号输入,另一个则是管自动空调的微调节信号输入。。。
大灯开关。。。
因为我只要搞自动灯,所以,开拆大灯开关。。。搞清楚原理很重要
拆下的大灯组合开关,前面的样子
拆开后的后面线路
滑片,连通不同的印刷电路就实现不同的功能选择。
组合灯开关电路板的正面。。
背面
逆向出的电路与功能,实际上自己在图内写得还不太正确,12号线接地时:大灯亮、小灯亮、仪表板背光亮、车尾示宽灯亮。。。所以,要完成 自动大灯功能,我只要把12号线的信号 弄成低电平就得了。。。挺简单的
然后测出的各线功能 与电压,5号线蓝线是个很奇怪的信号,似乎是个监测灯光系统的信号电平,只要有任何灯亮(包括室内灯、仪表灯),就会高电平,所有灯关闭后又才延时十几到几十秒才变低电平,我猜这可能是为防止疏乎忘记关灯用的检测电平信号
顺便,白线(12号线)接地时的电流值,这个很重要,到时电路怎么个驱动法,与这个电流与电压的关系很大,现在看这明显是个电脑芯片的高电平信号,接地电流只有2.01mA....嗯
然后是光敏电阻,做光线检测器用,从家里小猴子已经不玩的一个旧玩具上拆下的。。。。把它接了万用表带在车上,测了几天不同光线下的电阻变化情况,并记录下来。。。。基本确定是100K阻值时的光线适合开灯了,20-30K时的光线可以关灯了
上次做了不理想的纯硬件版本自动 灯。。。。存档。。
纯硬件版本的电路。
这回改单片机后的电路设计,用339的两个单元做迟滞比较器,检测光电阻分压出的电平,形成开关灯信号电平,然后接到带弱上拉电阻的单片机输入输出口,这样可以少焊几个上拉电阻,然后小灯信号电平进行分压后经339另一单元形成手动/自动/关闭自动灯系统的切换的信号电平,也接到单片机带弱上拉电阻的输入输出口,然后物尽其用,339的最后一个单元用于做输出,通过比较来自单片机的输出执行对白线(12号线)电平是否下拉,其实真正来说,应该一个两单元的393就可以够用了,可我手头正好没有393,只有339啊,所以就做成了这样的奇怪电路,,然后再在单片机上随便选个有弱上拉电阻的输入输出口做系统是否取得灯光控制权的指示灯,所有信号均低电平有效,因为51单片机启动后默认是置高电平的,不用P0口,是因为P0是三态口,没上拉电阻,用起来麻烦。。。。
然后是单片机的仿真,确实还是protues强,仿真实验中的运行时间与实际几乎一致,因为我做了上电启动后延时5秒开始工作,所以时间真实很重要,不像,MULTISIM,有高频件仿真时的运行时间步长啊,根本没法用。。。。但protues纯E文版的,不那么容易看懂,总算这几年一直很努力地背单词,磕磕碰碰的也很快把protues用上了手。。
设计时对这电路为什么要这样设计的几个想法的要点:
1、LM339是集电极开路输出,所以相当于有一个小三极管隔离,所以在不加上拉电阻的情况下,可以直接给大灯信号线做电平下拉(12V信号白线)输出,不怕倒灌。
2、339的低电平陷电流可高达16毫安,而实测了那个大灯开电平接地时电流只有2.01毫安,所以直接驱动的接法在电流上分析也是安全的。
主控处理器芯片(MCU)用的是写入相对比较麻烦的ATMEL89c52,原因是从网上资料分析他的功能虽然比STC弱,程序也只能用12v烧录的办法写进,但他的稳定性与抗干扰性都要比STC的系列强。。。至于为什么不用有内置晶振的8脚小单片机,是因为想做C51学习试验啊,所以希望从最经典的基本系统做起,从一个一个零件焊起,而不仅仅只是完成一个自动大灯系统,当然,以后需要的话,程序代码也完全可以移植到8脚小芯片的51单片机上运行嘛。。。因为自己编程有个尽量易移植的原则,所以写代码时尽量不使用某种型号单片机特有的特殊寄存器,也尽量不使用某种型号单片机特有的特殊命令,这也是以前大学时自学电脑C编程时养成的习惯,尽量不去直接调用特殊的操作系统中断,这样写代码的好处就是,换个编译器重新编译一下,程序就可以在不同操作系统的不同平台上都跑得稳稳当当。
同样的陷电流问题,AT89C52明显要小得多,低电平输出时单个PIN只能灌入10mA,整个芯片灌入不能大于71mA,不过,不过,实际上单片机论坛的好多人才做过破坏性实验了,现在的52机芯片,单个脚灌到20mA都没坏呢。。。据说是制造工艺改进了。。。嗯嗯,跑远了,我想在一个引脚上用低电平灌入个5mA电流用于做工作指示灯,显然这是很安全的。。。
然后是写代码编程。。。可能是因为有C编程的基础吧,我觉C51编程其实比较易上手,而且内外部操作资源也远远简单于以前的电脑编程,但比电脑编程麻烦的是,它没有那么多的函数封装,很多底层操作都是要自己直接写代码来完成的,不过,只要有相应的硬件规格表,边做边对照,花点时间,还是可以做的。。。。
写完代码编译好,在PROTUES的电路图上进行仿真上通过后,准备烧入单片机,为此还专门去淘宝订了个芯片烧录器回来,哈哈,其实应该也是可以自己照着网上的电路焊硬件,再照网上的成熟代码灌入的,而且好像上位机也有现成的。。只是我目前水平还太差了,再加上做这个东东我觉得意义并不大,做个网络代码搬运工实在没意义。。。。所以,直接拍一个省心快捷。。。
第一次烧写程序,觉得挺好玩,而且感觉与STC的ISP方式烧录速度其实也差不了多少,只不过,这个是要用12v烧录的。。。
很快把自己写好的程序烧进去了。。。
烧好代码的芯片扔到STC的实验板上看看,测试下烧进去的程序能不能正常运行。。。因为这实验板上对单片机所有的IO端口都做了引出,所以测下相应引脚
电压的变化,并试着拉低相应信号输入脚的电平,再测量程序中设定的输出引脚的电平,就可以测出程序有没有正常运行了。
哈哈,正常运行了咧!比较开心!
软件与主控芯片弄好了,是时候焊外围电路了,PROTUES的PCB排版啊。。。。感觉比MULTISIM差好多,虽然MULTISIM的pcb排版也不咋滴,但显然,这个更不咋滴。。。
可能搞软件的人不喜欢搞硬件?不过,我可只能自己排,而且还只能用洞洞板来焊。。。嗯,因为我上淘宝去问了,人家根本不想鸟我这样的小生意。。。问了几家PCB打样的,一听说是自己玩用的,而且只打最小数量的,都懒得理我了。。。好吧,那就自己慢慢焊呗。。。
焊好后装上主控芯片。
先试运行下。。。哈哈,看起来正常运行了。。。
开始拆车。。。做光线感应与指示灯的电路板。。。哈哈,我终于可以塞上那两个不顺眼很久的前台小孔了。。。
顺便发现,原来门控的接收器是装在这下面的。。。。
顺便的还有。。。防盗指示灯!也拍个,一闪一闪亮晶晶的那个。。。
中控前面的大板拆下后,拿上楼,回家慢慢整。。。三个孔,一个原厂已经做防盗指示灯用了,另两个就是我要填的,一个用做光线感应头,一个用做工作指示灯!
原先我是用这样的小蓝灯做工作指示灯的。。。很漂亮。。。。但是这个不是散射光的led,所以当时为保证侧面看的视觉效果,强行拉大了点电流用了,但结果两天后就烧掉了。。。。
做好的感应器与指示灯电路板
装上。。。。。
然后把拆下的前照灯组合开关也拆开,嗯嗯,我要引出大灯开关信号线与小灯开关信号线啊。。。。哈哈哈,天助我也,发现背板上有大把空间,就是小巧了点。。。所以剪了小小一块电路板装进去。。。然后用针捅出相应的孔,然后把接头插座焊上去。。。
再到开关电路板的前面焊点去飞线。。。把12号脚的线(大灯开关信号)与6号脚的线(驻车灯开关信号,也就是我们通常讲的小灯)用漆包线飞出。。。。
再把飞线焊到刚才前面做好的小电路板引脚上,这样就把这两路信号引出了,这样做虽然比直接在外面用并线器真接并原车线束麻烦,但以后坏了的话,换起来却比在外面直接并线要容易且成本低,因为换线束的成本要比换开关的成本要高多了,难度也大得多,而且换个开关可以自己干,但换线束可自己干不了啊。。。所以两害取其轻,我捣腾开关!飞好线后滴上上704硅橡胶,加固与绝缘。。。
装回,看看,就是背面多了一组接口,其他无损咧。。。
第二天胶一干,就去车库试下先,哈哈。。。完美运行。。。注意看啊。。。现在还是小蓝灯做指示灯的呢,还没烧掉
为确保正常,就这样裸板运行了两天多,主要是想着方便调节开关灯的设置电压值,结果。没想到设计的电压设置很合适,开关灯的外界光线亮度时机很让人满意,可能是原来就带着那个光电阻一路测阻值一周的原因吧,所以电路动作的阀值参数预置得很合适 的。。。。
开始找壳,装那块小小电路板,哈哈,以前折腾玩的开关电源的盒子,发现有一个很合适啊,果断用上!
钻三个小孔,电路板就稳稳安全地在里面了。。
然后又是拆车,这回拆的是驾驶座下方面。。。发现有个很大的空间可以随便装呢,最后后考虑固定方案,要稳固又方便以后维修。。。
想清楚就开动,用厚厚的L型铁架加螺丝固定。。。顺便,地线就直接接在这上面了。。。哈哈哈。。
地线与固定架。。。。
电源依旧从1/4电窗保险口取,因为我已经在那加了保险取电线了,而且原线路、新加线路都加了保险,还预留了一组接线口,所以加多一组线非常快。。。拆下原来保护袋,把线插入,旋紧压脚就可以了。。。
再原样装回。。。妥妥的。。
然后装前面的主控盒子。。。。
最后盖上盖子。。。。
装回。。。试车。。。。日,忽然发现小蓝灯不亮了,但自动大灯的运行还是一切正常。。。
显然,LED烧了。。。真郁闷,只好再把前板拆下。。。重新换LED。
吸取 教训,这回用了雾化发光的小黄灯了。。。这样小小电流就能从侧面看到灯光了,嗯,这回也很老实了,不敢加大电流用了,只用到了2mA ,限流电阻取了个1.5K,供电是7805.。。。应该是2.1毫安。。。。
还特意先在室内接电接电流表试了一个上午,怕他不稳定又烧,拆中控台前面板可麻烦了。
顺便也把这路电路板的接口做成了接插件,这样就可以哪里不行拆哪里,不用为换一个灯,一路从中控台拆到下饰板了。
换上的小黄灯。。。。三个孔都填充上了,看起来就得顺眼了。
副产品,上回加点烟器与USB充电路上加的线,原来塞在这条缝里的,这回直接可以藏到前板下面去了,美观大方!
六点多,天暗下来,哈哈,自动开灯。。。工作指示灯也亮。。。
一路过各种光源,包括路上那种有爆闪拍照的摄像头。。。。都很正常。。。哈哈哈哈哈哈哈哈,而大灯一亮,仪表板、示宽灯也自动亮。。。所以,一根线就实现了完美控制。
感觉通用太坑了,估计原厂电脑里面自动灯自动空调等等功能都是有的,只是配置被屏蔽了。。。。所以本来加一个感应器再设置下下电脑就可以完成的事,硬生生让我多做了一个控制系统 上去。。。哈哈
最后,软件程序的源码,这是自己写的第一个投入实用的单片机程序代码,同时,所有操作方法说明与解释,也都一一详细注在源码中了,方便以后维修:
//main.c
#include <reg52.h>
//单片机对外的所有输入输出信号,均以低电平有效,即 0开 1关。
//89c52单片机,使用A(MHz)晶振,计时器中断定时B(ms)时的初值公式为:
//TH0=(65536-A*B/12*1000)/256;
//TL0=(65536-A*B/12*1000)%256;
//本程序默认使用at89c52芯片与11.0592MHz晶振,定时50ms,初值公式为:
//TH0=(65536-46080)/256;
//TL0=(65536-46080)%256;
sbit bright_enough=P1^0;
sbit dark_enough=P1^1;
sbit control_mode=P1^4;
sbit light=P1^5;
sbit work=P2^0;
unsigned char num1,num2,dark_detect,bright_detect;
//开关监测部分的代码,由定时器0计数,当开关打到驻车档时开始计时。
void T0_time(void) interrupt 1//定时器0,用于开关模式检测计时,每50ms计数一次,
//仅当control_mode==0发生时启动,num1==20时为一秒。
{
TH0=(65536-46080)/256; //T0定时器装初值,
TL0=(65536-46080)%256;
num1++;
}
void delay_ms(unsigned int t)
{
unsigned int i,j;
for(i=t;i>0;i--)
for(j=110;j>0;j--);
}
void control_mode_scan(void) //对驻车灯开关动作的检测,
//并用计时器1对动作需求进行计时分析
{
if(control_mode==0)
{
delay_ms(10);
if(control_mode==0)
{
num1=0; //每0.5ms加1的计数变量置初值0
TH0=(65536-46080)/256; //T0定时器装初值,
TL0=(65536-46080)%256;
TR0=1; //启动定时器0
while(!control_mode) //当旋钮转到小灯档时
{
if(num1>30)//若1.5秒内不离开小灯开启档,则认为是要手动控制灯光(只开小灯),
//所以自动功能关闭,控制权交手动。
{
work=1; //自动大灯系统停止工作
light=1; //不论大灯灯光原在何状态,直接关闭大灯灯光。
num1=31; //随便给个大于30的值,防止旋钮长时间停在小灯档,
//计数累加超出unsigned char范围,导致出现不可预测的问题
}
}
if(num1<=30) work=~work; //若旋钮转到小灯档又在1.5秒内离开小灯档,
//则进行自动灯控制关闭与开启的切换。
TR0=0; //关闭定时器0
}
}
}
//光线监测部分的代码,由定时器1计数,每0.5秒检测一次
void T1_time(void) interrupt 3 //定时器1用于持续检测光线变化并取得检测结果,
//每50ms秒中断一次,num2==10时为0.5秒
{
TH1=(65536-46080)/256;
TL1=(65536-46080)%256;
num2++;
}
unsigned char count_1_bits(unsigned char value)//计算一个字节中1 的个数并返回
{
unsigned char num=0;
while (value)
{
if (value%2==1) num++;
value>>=1;
}
return num;
}
void dark_bright_scan(void) //每0.5秒,即每当num2==10时检测处理一次
{
unsigned char dark,bright;
if(num2>=10)//超过0.5秒,这样是为确保本语句能有效执行到,用于防止MCU正好在
//执行其他动作时num2==10,确保不会错过0.5秒以后这个大致时点。
{
TR1=0; //关闭定时器1
dark_detect<<=1;
/*dark_detect左移一位,此处dark_detect是作为一个8位长的队列来存储4秒内光线
检测结果因为dark_detect是unsigned char类型,字长8bit,每0.5秒间隔的光线检
测结果用一位来表示,1表示够暗可开类,0表示不够暗不用开灯。所以dark_detect
总共可以存储8个时点的光线检测结果,而中断是每半秒发生一次,所以在程序持续
运行阶段,dark_detect内就一共存储了最近4秒内的光线暗度检测结果dark_tetect
初始值为0b00000000,而dark_tetect左移 一位后,就把3.5秒前检测的那次结果
(最高位)丢掉,同时把最低位补零,等待计入最新检测结果。*/
if(dark_enough==0) dark_detect|=1;
/* 若该时点光线够暗,即当dark_enough(P1^1)低电平时,表示当前光线暗度已经达
到预设的开灯阀值的光线暗度,就要把dark_tetect与1(0b00000001)进行按位“或”,
这样就把当前光线够暗的情况记录在dark_tetect的最低位中而若当前光线不够暗到
开关预计的阀值,即当dark_enough(P1^1)处高电平态时,对左移后的dark_tetect不
作处理,直接使用左移时的补0作为状态记录。这样随着每次进中断对dark_tetect的
持续左移,dark_detect的二进制码中就存储了最近4秒的光线够不够暗的状态信息。*/
bright_detect<<=1; //不再解释,亮度检测的原理同暗度检测完全一样
if(bright_enough==0) bright_detect|=1;
dark=count_1_bits(dark_detect);
bright=count_1_bits(bright_detect);
if(!work) //仅在自动灯系统允许工作时做开关灯动作。
{
if(dark>4) light=0;//检测出4秒内环境光线够暗的次数大于4次,开灯,
//此句同时有副效应就是环境够暗时延时2.5秒才开灯。
if(bright>4) light=1;//检测出4秒内环境光线够亮的次数大于4次,关灯,
//此句同时有副效应就是环境够亮时延时2.5秒才关灯。
//以上两项条件都不符合时,不执行任何动作,维持灯光先前原态。
}
else
light=1;//若自动灯系统在不允许工作状态,大灯直接关闭。
num2=0; //将数值已达到10以上的num2进行清零
TH1=(65536-46080)/256; //T1定时器装初值,
TL1=(65536-46080)%256;
TR1=1; //启动定时器1
}
}
void main(void)
{
num1=num2=dark_detect=bright_detect=0;
work=light=1;
TMOD=0X11; //设置定时器0 和定时器1为工作方式1(0001 0001)
TH1=(65536-46080)/256;
TL1=(65536-46080)%256;
TH0=(65536-46080)/256;
TL0=(65536-46080)%256;
EA=1; //开总中断
ET1=1; //开定时器1中断
ET0=1; //开定时器0中断
TR0=1; //启动定时器0
while(num1<20*5);//上电后延时5秒,给司机发动汽车的时间
TR0=0; //关闭定时器0
work=0;
TR1=1; //启动定时器1
while(1)
{
control_mode_scan(); //按预设开关动作模式检测,随时转换自动灯是否工作。
dark_bright_scan(); //按预设阀值对环境光线进行检测并得出最终开关灯结果。
}
}
end
后续:
间接解决,自制gl8自动大灯系统在发动机启动前不进入开灯程序
http://bbs.mydigit.cn/read.php?tid=1919324