分享

自行车测速、测距仪原来是这么玩的

 魅影苍穹 2016-06-28

“生活,就想骑自行车。


要保持平衡,就得不断往前走。”

你是否看过一部叫《骑动人生》的电影呢,在环法大赛中,自行车不仅仅是一项运动,也是许多许多人寻找自我人生的挑战。
 
当然,今天的主角不是自行车,我们也不是专业的自行车骑手,但我们可以从另一个角度来玩自行车,那就是给自行车增加一些辅助功能——测速/测距。 

1
项目简介

今天,小编教大家制作一个自行车测速、测距仪。具有测量自行车行驶的瞬时速度和累计路程的功能。把它放在自行车的龙头上面,霸气十足,而且对于想要通过骑车运动的人来说,可以很好的把握自己骑行的速度和路程,给单调的骑车带来趣味性。呐,如下图,拉风不!



     图1.1  安装好之后的自行车测速测距仪
 
图1.2 (速度2.388米/秒 距离:2334.52米)

2
项目制作教程
2.1硬件准备
在制作该项目之前首先得准备一些必要的硬件。他们分别是单片机最小系统板,1602液晶,霍尔传感器、磁钢和电池(没有充电电池可以使用电池盒加普通5号电池)。  

 图2.1 单片机最小系统   


图2.2 1602液晶

   

图2.3 霍尔传感器和磁钢


图2.4 电池


准备好了这些东西之后,就可以非常happy的开始制作了。当然这里的电池,小编使用的是充电电池。因为刚好拆掉一辆遥控小车,所以有了这块充电电池。其实使用电池盒加电池更加便宜和实惠,更加适合入门的人。

 
2.2 原理讲解

下面稍微讲解一下测速与测距的原理。首先是霍尔传感器的原理。如图2.5所示当霍尔传感器没有处于强烈的磁场中的时候,它的信号引脚输出的是高电平,反之,当有强烈的磁场时,它输出的是低电平。


 图2.5 霍尔传感器的接线图


图2.6 连接好的霍尔传感器


从图2.5中,大家也看出了霍尔传感器的接法。最左边的引脚为Vcc电源引脚,中间的引脚为GND引脚,最右边的引脚为输出引脚。这里解释一下为什么输出引脚要有一个电阻接到Vcc上面。因为霍尔传感器的输出引脚是漏极输出,所以当需要输出高电平时必须要接上拉电阻到Vcc电源。这里的上拉电阻可以使用1K--10K的电阻。小编使用的是10K的电阻。知道了霍尔传感器的最用之后,具体应该怎么用上去呢?可以这样做,把磁钢(其实就是产生磁场的磁铁)固定在钢圈上面,把霍尔传感器固定在自行车的钢轴上面,如图2.7所示。                                         

 


图2.7 磁钢的安装和霍尔传感器的安装


那么,当每旋转一圈,当磁钢运动到霍尔传感器附近时,霍尔传感器输出低电平,当磁钢远离霍尔传感器时,霍尔传感器输出高电平。也就是说自行车轮胎每旋转一圈,霍尔传感器就输出一次低电平。那么只要测出两次输出低电平之间的时间间隔,然后再结合自行车轮胎的周长就可以很轻松的求出速度。而对于自行车的行驶里程,只要记录下霍尔传感器输出低电平的次数,再乘以周长就可以求出了。


连接好了霍尔传感器和磁钢,接着把剩下的单片机最小系统板和液晶连接正确。(这里就不详细介绍单片机如何驱动1602液晶的了。有疑问的朋友可以自己百度搜索一下,不难的。)如图2.8,是连接好的单片机系统和液晶。



图2.8 连接好的电池,单片机和液晶



图2.9 已经固定好在自行车上的所有硬件


2.3 程序编写

下面小编讲解一下程序的编写。

     

这里我把1602的操作封装在Star1602.c源程序中,在主函数中包含Star1602.h即可。(Star1602.h和Star1602.c在本教程的最后给出)

     
#include
#include
#define CIRCLE  1.8   //宏定义 车轮的周长(这个要根据实际的车轮进行设置)
sbit Signal = P1^0;   //这里的Signal表示的是霍尔传感器的信号引脚 
int m_second=0; //定义变量m_second,用来记录时间(以毫秒为单位)
float speed=0.0 ;  //定义速度变量 
float length=0.0 ;//定义路程变量
void main()
{
    lcd_init(); //初始化液晶函数
TMOD = 0x01; //打开定时器0,并设定其工作方式为16位定时模式。
TH0=(65536-10000)/ 256;
TL0=(65536-10000)% 256;   //设定定时器的初值,使得没10ms中断一次
EA = 1;   //允许总中断
ET0 = 1;   //允许定时器0终端
TR0 = 1;  //启动定时器0
while(1)     //大循环
{
    while(Signal); //等待霍尔传感器信号线拉低;
speed = CIRCLE *1000 / m_second ; //计算速度。
m_second = 0;   //计时清零
length +=  CIRCLE ; //路程加一个车轮周期
//第一行,显示速度
lcd_pos(0x0);  //设定液晶的写入位置为第一行第一格
lcd_wdat(‘S’);
lcd_wdat(‘p’);
lcd_wdat(‘e’);
lcd_wdat(‘e’);
lcd_wdat(‘d’);
lcd_wdat(‘:’);
lcd_wdat( (int)speed%10 );        //显示速度的整数部分
lcd_wdat( (int)(speed*10)%10 );   //显示速度的小数第一位
lcd_wdat( (int)(speed*100)%10 );   //显示速度的小数第二位
lcd_wdat(‘m’);
lcd_wdat(‘/’);
lcd_wdat(‘s’);
 
        //第二行,显示里程
lcd_pos(0x80);  //设定液晶的写入位置为第二行第一格
lcd_wdat(‘L’);
lcd_wdat(‘e’);
lcd_wdat(‘n’);
lcd_wdat(‘g’);
lcd_wdat(‘t’);
lcd_wdat(‘h’);
lcd_wdat(‘:’);
lcd_wdat(length /10000+0x30);    //显示里程的万位;
lcd_wdat(length %10000/1000+0x30);    //显示里程的千位;
lcd_wdat(length %1000/100+0x30);    //显示里程的百位;
lcd_wdat(length %100/10+0x30);    //显示里程的十位;
lcd_wdat(length %10+0x30);    //显示里程的个位;
lcd_wdat(‘m’);
}
 
}
void  timer0_intt()  interrupt 1   //
{
TH0=(65536-10000)/ 256;
TL0=(65536-10000)% 256;   //设定定时器的初值,使得没10ms中断一次
m_second += 10;  //因为中断每10毫秒一次,所以这里每次加10;
}
 
附件1 Star1602.h 
#ifndef __STAR1602_H__
#define __STAR1602_H__
 
sbit rs= P2^0;      //
sbit rw = P2^1;  //     
sbit ep = P2^2;  //
 
 
void  lcd_init();                  //液晶初始化函数
void  lcd_pos(unsigned char pos);   //设定液晶的显示位置函数
void  lcd_wdat(unsigned char dat);  //液晶写入字符
void  lcd_write_int(unsigned int x);     //液晶显示一个整形变量
 
#endif
 
附件2 Star1602.c
#include 
#include '1602.h'
 
/*****************************************************************************
函数功能:LCD延时子程序
入口参数:ms
出口参数:
*****************************************************************************/
static void delay(unsigned char ms)
{
unsigned char i;
while(ms--)
{
for(i = 0; i<>
}
}
 
/*****************************************************************************
函数功能:测试LCD忙碌状态
入口参数:
出口参数:result
*****************************************************************************/
static bit lcd_bz()
{
bit result;
rs = 0;
rw = 1;
ep = 1;
delay(5);
result = (bit)(P0 & 0x80);
ep = 0;
return result;
}
 
/*****************************************************************************
函数功能:写指令数据到LCD子程序
入口参数:cmd
出口参数:
*****************************************************************************/
static void lcd_wcmd(unsigned char cmd)
{
while(lcd_bz()); //判断LCD是否忙碌
rs = 0;
rw = 0;
ep = 0;
delay(5);
P0 = cmd;
    delay(5);
ep = 1;
    delay(5);
ep = 0;
}
 
/*****************************************************************************
函数功能:设定显示位置子程序
入口参数:pos
出口参数:
*****************************************************************************/
void lcd_pos(unsigned char pos)
{
lcd_wcmd(pos | 0x80);
}
 
/*****************************************************************************
函数功能:写入显示数据到LCD子程序
入口参数:dat
出口参数:
*****************************************************************************/
void lcd_wdat(unsigned char dat)
{
while(lcd_bz()); //判断LCD是否忙碌
rs = 1;
rw = 0;
ep = 0;
P0 = dat;
    delay(5);
ep = 1;
    delay(5);
ep = 0;
}
 
/*****************************************************************************
函数功能:LCD初始化子程序
入口参数:
出口参数:
*****************************************************************************/
void lcd_init()
{
lcd_wcmd(0x38);
delay(100);
lcd_wcmd(0x0c);
delay(100);
lcd_wcmd(0x06);
delay(100);
lcd_wcmd(0x01);
delay(100);
}
/*****************************************************************************
函数功能:LCD写入一个整形数据
入口参数:int x
*****************************************************************************/
void  lcd_write_int(unsigned int x);   
{
unsigned char x1,x2,x3,x4,x5;
x1 = x/10000;
x2=x%10000/1000;
x3=x%1000/100;
x4=x%100/10;
x5=x%10;
    lcd_wdat(x1+0x30);
lcd_wdat(x2+0x30);
lcd_wdat(x3+0x30);
lcd_wdat(x4+0x30);
lcd_wdat(x5+0x30);
}

写在最后的话
都说电子是玩出来的,小编觉得,我们大可不必循规蹈矩的按照某些教程上的练习题来做项目,把眼光放入日常生活中,解决生活实际问题才是最好的练习,并且能够在这过程中享受到快乐和成就感。


特别推荐



 为什么电梯不使用单片机而使用PLC? 


【干货】 从头到脚解剖LED



人人必备 PCB设计宝典



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多