测速模块
对射式测速模块:有遮挡的时候会输出高电平;没遮挡的时候会输出低电平,根据之前小车安装的测速片,结合这个测速模块,通过计算高低电平变化的频率,就可以计算出速度!
测速的具体计算:
综上所述,目的就是根据高低电平来计算出速度:
轮子走一圈,经过一个周长, C = 2x3.14x 半径 = 3.14 x 直径( 6.5cm )对应的码盘也转了一圈,码盘有 20 个格子,每经过一个格子,会遮挡(高电平)和不遮挡(低电平), 那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM ,定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm。假设一秒有 80 脉冲,那么就是 80cm/s
模块组装
将模块粘在测速轮的两边,将VCC和GND接到单片机引出的面包板的正负极,并将OUT口接到P2.1 (后改为P3.2,见之后的说明)
代码实现:
speed.c:
#include "reg52.h"
#include "UART.h"
sfr AUXR = 0x8E; //配置了这句话,才可以在UART的初始化里写AUXR寄存器,原因见STC89系列的手册
sbit speedOUT = P2^1;
int cnt_timer = 0;
int cnt_pulse = 0;
int speed = 0;
void Timer0Init(void)
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x4C; //设置定时初值 //50ms
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1; //打开中断!
}
//timer0的中断处理程序 //中断程序一般写在main函数的后面 //定时器0溢出时将触发这个中断函数
void speed_inter() interrupt 1
{
cnt_timer++;
TL0 = 0x00; //设置定时初值
TH0 = 0x4C; //设置定时初值 //50ms
if(cnt_timer == 20){//经过(20*50毫秒 =)1秒
cnt_timer = 0;
speed = cnt_pulse;
Send_string("speed = ");
if((speed/10) == 0){//说明speed是个位数
Send_byte(speed + 0x30);
}else{//说明speed是两位数
Send_byte(speed/10%10 + 0x30);//“速度的十位”显示字符型数字
Send_byte(speed/1%10 + 0x30);//“速度的个位”显示字符型数字
}
Send_string("cm/s\r\n");
cnt_pulse = 0;
}
}
void pulse_detect()
{
while(speedOUT == 1);//等待OUT变成低电平,即等待一次不遮挡
while(speedOUT == 0);//等待OUT变成高电平,即等待一次遮挡
cnt_pulse++; //此时经过一个脉冲
}
uart.c:
#include "reg52.h"
#include <motor.h>
#include <string.h>
#define SIZE 12
sbit D5 = P3^7;
sfr AUXR = 0x8E;
char buffer[SIZE];
/*
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
AUXR &= 0xBF; //定时器时钟12T模式
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xFD; //设置定时初始值
TH1 = 0xFD; //设置定时重载值
ET1 = 0; //禁止定时器中断
TR1 = 1; //定时器1开始计时
}
*/
void UartInit() //9600bps@11.0592MHz
{
SCON = 0x50; //配置串口工作方式1,REN使能接收
//配置定时器1,工作方式为8位自动重载
TMOD &= 0x0f;//定时器1工作方式位8位自动重装
TMOD |= 0x20;
TH1 = 0xFD;
TL1 = 0xFD; //9600波特率的初值
TR1 = 1;//启动定时器
ES = 1;//开启串口中断
EA = 1;//开启总中断
}
void Send_byte(char data_msg)
{
SBUF = data_msg;
while(TI == 0);//在请求中断时,TI= 1,既!TI=0,等待数据的发送完成;响应中断结束后TI = 0,既!TI = 1,将TI清零
TI = 0;
}
void Send_string(char* str)
{
while(*str != '\0')
{
Send_byte(*str);
str++;
}
}
//TI 发送请求中断标志位,是指单片机向电脑发送
//RI 接收请求中断标志位,是指单片机接收电脑的消息
main.c
#include "reg52.h"
#include <delay.h>
#include <motor.h>
#include <uart.h>
#include <speed.h>
sbit led2 = P3^6;//根据原理图(电路图),设备变量led2指向P3组IO口的第6口
void main()
{
Timer0Init();
UartInit();
while(1)
{
Motor_front();
pulse_detect();
}
}
实现效果
由于使用了串口来不断发送速度信息,因此可以使用串口软件上接收:
一些问题
以上代码虽然可以实现期望的效果,但是也有一些问题,就是由于“paulse_detect"函数使用了两个while(),会导致一旦调用了这个函数,那么除了中断之外的方法都无法让程序执行其他功能了,在这个单纯实现测速的代码中这样做没有问题,但是如果想要在测速的基础上再实现一些其他的功能,那“paulse_detect"函数的写法就不利于功能的扩展了....
解决办法就是将测速传感器的脉冲检测的OUT口也配置成一个中断,即使用P3.2口的外部中断(但是P3.2之前用于给电机供电,所以别忘记还需要修改一下电机部分的sbit定义!!),这样既可以保留测速的功能,也不会影响main函数之后可能进行的其他操作。
同时需要注意,配置外部中断时,应该配置成下降沿触发而不是低电平触发:
由参考手册得知
即IT0 = 1,配置外部中断0下降沿触发中断
同时,对于speed的显示,刚刚对于每一位的提取再转化为字符的行为很麻烦,可以使用sprintf(speed_real,"speed: %d cm/s\r\n",speed); 来直接构建字符串,但是记得提前定义一个char speed_real[24],并添加stdio.h的库!
接下来看看修改后的代码:
motor.c中的新定义:
sbit RightconlA = P3^6; //B-1A
sbit RightconlB = P3^3; //B-1B
sbit LeftconlA = P3^4; //A-1A
sbit LeftconlB = P3^5; //A-1B
speed.c:
#include "reg52.h"
#include "UART.h"
#include "stdio.h"
sfr AUXR = 0x8E; //配置了这句话,才可以在UART的初始化里写AUXR寄存器,原因见STC89系列的手册
int cnt_timer = 0;//定时器统计计数
int cnt_pulse = 0;//统计格子,脉冲次数
int speed = 0;//速度
int flag;
void Timer0Init()
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x4C; //设置定时初值 //50ms
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1; //打开中断!
}
void EX0_Init() //外部中断0
{
EX0 = 1; //打开外部中断
//EA = 1 //总中断(不过在串口初始化相关已经打开了总中断,所以可以在这里省略)
//因为测速模块的原理是“有遮挡,输出高电平;无遮挡,输出低电平”,而我们要在检测到有间隙的时候触发中断,既在有遮挡变换到无遮挡(高电平变到低电平)的瞬间检测
//既下降沿触发
IT0 = 1;//外部中断下降沿触发
}
//timer0的中断处理程序 //中断程序一般写在main函数的后面 //定时器0溢出时将触发这个中断函数
void speed_inter() interrupt 1
{
cnt_timer++;
TL0 = 0x00; //设置定时初值
TH0 = 0x4C; //设置定时初值 //50ms
if(cnt_timer == 20){//经过(20*50毫秒 =)1秒
flag = 1;
cnt_timer = 0;//当20次表示1s,重新让cnt_timer从0开始,计算下一次的1s
//计算小车的速度,也就是拿到cnt_pulse的值
speed = cnt_pulse;
cnt_pulse = 0;//1秒后拿到cnt_pulse个格子,就能算出这1s的速度,格子清零
}
}
void pulse_Inter() interrupt 0 //外部中断0,即P3.2口变低电平时会自动触发这个中断处理程序,即触发一次不遮挡就会进入中断
{
cnt_pulse++; //此时经过一个脉冲
}
UART.c:
#include "reg52.h"
#include <motor.h>
#include <string.h>
#define SIZE 12
sbit D5 = P3^7;
sfr AUXR = 0x8E;
char buffer[SIZE];
/*
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
AUXR &= 0xBF; //定时器时钟12T模式
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xFD; //设置定时初始值
TH1 = 0xFD; //设置定时重载值
ET1 = 0; //禁止定时器中断
TR1 = 1; //定时器1开始计时
}
*/
void UartInit() //9600bps@11.0592MHz
{
AUXR = 0x01;
SCON = 0x50; //配置串口工作方式1,REN使能接收
//配置定时器1,工作方式为8位自动重载
TMOD &= 0x0f;//定时器1工作方式位8位自动重装
TMOD |= 0x20;
TH1 = 0xFD;
TL1 = 0xFD; //9600波特率的初值
TR1 = 1;//启动定时器
ES = 1;//开启串口中断
EA = 1;//开启总中断
}
void Send_byte(char data_msg)
{
SBUF = data_msg;
while(TI == 0);//在请求中断时,TI= 1,既!TI=0,等待数据的发送完成;响应中断结束后TI = 0,既!TI = 1,将TI清零
TI = 0;
}
void Send_string(char* str)
{
while(*str != '\0')
{
Send_byte(*str);
str++;
}
}
//TI 发送请求中断标志位,是指单片机向电脑发送
//RI 接收请求中断标志位,是指单片机接收电脑的消息
//M1前进 M2后退 M3左 M4右
void Uart_Handler() interrupt 4
{
static int i = 0;//静态变量,被初始化一次
char temp;
if(RI == 1)//中断处理函数中,对于接收中断的响应
{
RI = 0;//清除接收中断标志位
temp = SBUF;
if(temp == 'M')
{
i = 0;
}
buffer[i++] = temp;
if(buffer[0] == 'M')
{
switch(buffer[1])
{
case '1':
D5 = 0;
Motor_front();
break;
case '2':Motor_back();
break;
case '3':Motor_Left();
break;
case '4':Motor_Right();
break;
default:
Motor_Stop();
break;
}
}
if(i == 12)
{
memset(buffer,'\0',SIZE);
i =0;
}
}
if(TI)
{
}
}
main.c:
#include "reg52.h"
#include <delay.h>
#include <motor.h>
#include <uart.h>
#include <speed.h>
#include "stdio.h"
sbit speedOUT = P3^2;//外部中断0
sbit led2 = P3^6;//根据原理图(电路图),设备变量led2指向P3组IO口的第6口
extern int flag;
char speed_real[24];//主程序发送速度数据的字符串缓冲区
extern int speed;//速度
void main()
{
Timer0Init();
UartInit();
EX0_Init();
while(1)
{
if(flag)
{
sprintf(speed_real,"speed: %d cm/s\r\n",speed); //构建字符串
Send_string(speed_real);
flag = 0;
}
}
}