您现在的位置是:首页 >学无止境 >基于pic16f877a的车辆里程记录与速度测量网站首页学无止境
基于pic16f877a的车辆里程记录与速度测量
本文利用测速传感器模块对车轮的码盘进行计数,通过简单的数据处理可以转化成里程数与速度值。
首先是测速模块的硬件(窄体的槽型光耦测速模块,在陶包x信可以买到)
使用说明:
1、模块槽中无遮挡时,接收管导通,模块DO输出低电平,遮挡时,DO输出高电平;
2、DO输出接口可以与单片机IO口直接相连,检测传感器是否有遮档,如用电机码盘则可检测电机的转速。
3、模块DO可与继电器相连,组成限位开关等功能,也可以与有源蜂鸣器模块相连,组成报警器。
接线
1、VCC接电源正极3.3V-5V
2、GND接电源负极
3、D0(TTL开关信号输出)接单片机外部中断
4、A0无效
电机码盘的摆放位置 让码盘放在俩个柱子之间 通电后发现当光线被挡住时l开关指示的led灯将熄灭
代码部分
代码部分有俩个要处理的数据,一个时里程数,一个是速度值
1里程数(里程值被存放在eeprom中可以防止断电丢失)
我这款电机码盘上面有20个小孔,经过测量发现一个车轮的周长为20cm,测速模块每经过一个小孔输出一个跳变,也就是说一个跳变对应的里程为1cm,那么10个跳变则为1dm=10cm,
利用pic16f877a的RB0外部电平中断,每次收到一个跳变则进入中断并将计数值count_temp+1
我设置的分度值为1dm,进入10次中断后里程数就+1
2速度值
速度值的处理方式为一个俩个小孔之间是距离除以时间
简单来说就是在一个小孔到来时进入中断将定时器打开 下一个小孔到来进入中断将定时器关闭,为了防止定时器溢出,定时时器溢出时将n值+1,数据处理时(n*65535再加上定时器的数据)作为时间值
然后将俩个小孔之间对应车轮走过的距离1cm作为距离值
即 距离值/时间值 再进行简单的单位换算即可得出速度值
代码基本封装成函数了 可以直接看函数的注释
//from 793k
//基于pic16f877a编写代码
#include <pic.h>
__CONFIG(0xFF29);
__EEPROM_DATA(0,0,0,0,0,0,0,0);//eeprom寄存器一开始给一个0的值
static volatile char table[10]={0x30,0x31,0x32,0x33,
0x34,0x35,0x36,0x37,0x38,0x39} ;
#define rs RA5
#define rw RA2
#define e RA3
//lcd对应的使能引脚,看自己的原理图,有些RA2改成RA4
unsigned int count_temp,n,speed;
unsigned char shuju[6];
unsigned char speed_data[4],m;
unsigned int t_speed;
union
{ unsigned long count;
char data[3];
}li_cheng;
//共用体
//========================lcd相关函数===============================
void delay()//延时函数
{unsigned int i;for(i=999;i>0;i--);}
void lcdml()//写入命令
{rs=0;rw=0;e=0;delay();e=1;}
void lcdx()//写入字
{rs=1;rw=0;e=0;delay();e=1;}
void lcd_Init()//lcd初始化
{
TRISD=0;TRISA=0;ADCON1=7;
PORTD=0;
delay();
PORTD=1;lcdml();
PORTD=0x38;lcdml();
PORTD=0x0c;lcdml();
PORTD=0x06;lcdml();
}
//==========================数据处理部分========================================
void licheng_deal()//里程数的数据处理
{
shuju[0]=li_cheng.count/100000;
shuju[1]=li_cheng.count%100000/10000;
shuju[2]=li_cheng.count%10000/1000;
shuju[3]=li_cheng.count%1000/100;
shuju[4]=li_cheng.count%100/10;
shuju[5]=li_cheng.count%10;
}
void speed_deal()//速度的数据处理
{
//speed为最后的速度,speed_data为lcd显示做的数据处理
speed=1000000/t_speed;
speed_data[0]=speed/1000;
speed_data[1]=speed%1000/100;
speed_data[2]=speed%100/10;
speed_data[3]=speed%10;
}
//========================================================================================
void interrupt int_serve()//中断服务函数
{
if(TMR1IF==1){n++;TMR1IF=0;}//防止速度部分TMR1模块溢出
if(INTF==1)
{
count_temp++;;
if(count_temp==10){li_cheng.count++;count_temp=0;}//当收到100个高电平里程数+1,100个高电平相当于小车走了1m
//====================================速度测量部分===============
m++;
if(m==0)//打开tmr1 清空定时器数据
{n=0;TMR1IE=0;TMR1L=0;TMR1H=0;TMR1ON=1;TMR1IF=0;}
if(m==1)
{TMR1ON=0;TMR1IE=0;t_speed=(n*65535)+(TMR1H*256)+TMR1L;TMR1IF=0;}
//===============================================================
INTF=0;//清电平中断标志位,不清的话会一直进中断出不去
}
}
void init()
{
t_speed=0;
TRISD=0;TRISA=0;ADCON1=7;
m=250;n=0;
TRISB0=1;count_temp=0;
GIE=1;PEIE=1;
TMR1IE=1;
TMR1IF=0;
INTE=1;INTF=0;
T1CON=0B00000001;
}
void main()
{
init();//中断&端口初始化函数调用
lcd_Init();//lcd初始化函数调用
li_cheng.data[0]=eeprom_read(0x00);
li_cheng.data[1]=eeprom_read(0x01);
li_cheng.data[2]=eeprom_read(0x02);
//从寄存器中调用上次存入的数据
while(1)
{ speed_deal();
licheng_deal();
eeprom_write(0x02,li_cheng.data[2]);
eeprom_write(0x01,li_cheng.data[1]);
eeprom_write(0x00,li_cheng.data[0]);
//将里程放到寄存体中 掉电不会丢失
PORTD=0x80;lcdml();
PORTD=table[shuju[0]];lcdx();
PORTD=table[shuju[1]];lcdx();
PORTD=table[shuju[2]];lcdx();
PORTD=table[shuju[3]];lcdx();
PORTD=table[shuju[4]];lcdx();
PORTD=table[shuju[5]];lcdx();
PORTD='d';lcdx();
PORTD='m';lcdx();
PORTD=0xc0;lcdml();//========================第二行速度的显示
PORTD=table[speed_data[0]];lcdx();
PORTD=table[speed_data[1]];lcdx();
PORTD='.';lcdx();
PORTD=table[speed_data[2]];lcdx();
PORTD=table[speed_data[3]];lcdx();
// PORTD='c';lcdx();
PORTD='m';lcdx();
PORTD='/';lcdx();
PORTD='s';lcdx();
}
}
最后附上Proteus仿真(利用电源输入时钟信号代替测速模块的运行)
输入100hz的时钟,对应1s100个跳变即100cm,所以对应的速度为1m/s,仿真结果正确。