目录
1. 测速模块介绍
2. 测试原理和单位换算
3. 定时器和中断实现测速开发和调试代码
4. 小车速度显示在OLED屏
1. 测速模块介绍
- 用途:广泛用于电机转速检测,脉冲计数,位置限位等。
- 有遮挡,输出高电平;无遮挡,输出低电平
- 接线 :VCC 接电源正极3.3-5V
- GND 接电源负极 DO TTL开关信号输出
- AO 此模块不起作用
2. 测试原理和单位换算
- 轮子走一圈,经过一个周长,C = 2x3.14x半径= 3.14 x 直径(6.5cm)
- 对应的码盘也转了一圈,码盘有20个格子,每经过一个格子,会遮挡(高电平)和不遮挡(低电平), 那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM
- 定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm
- 假设一秒有80脉冲,那么就是80cm/s
3. 定时器和中断实现测速开发和调试代码
定时器介绍:
- C51中的定时器和计数器是同一个硬件电路支持的,通过寄存器配置不同,就可以将他当做定时器 或者计数器使用。
- 确切的说,定时器和计数器区别是致使他们背后的计数存储器加1的信号不同。当配置为定时器使 用时,每经过1个机器周期,计数存储器的值就加1。而当配置为计数器时,每来一个负跳变信号 (信号从P3.4 或者P3.5引脚输入),就加1,以此达到计数的目的。
- 标准C51有2个定时器/计数器:T0和T1。他们的使用方法一致。C52相比C51多了一个T2
中断寄存器介绍:
CPU能响应定时器0中断的条件:需要配置IE寄存器的bit1: ET0 bit7:EA
- 1. ET0中断允许要置一 ET0 = 1
- 2. EA总中断要置一 EA = 1
测试数据通过串口发送到上位机
//main.c
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "reg52.h"
#include "time.h"
#include "stdio.h"sbit speedIO = P3^2;//外部中断0
unsigned int speedCnt = 0; //统计格子,脉冲次数
extern unsigned int speed;//速度
extern char signal; //主程序发速度数据的通知
char speedMes[24]; //主程序发送速度数据的字符串缓冲区void Ex0Init()
{EX0 = 1;//允许中断//EA = 1;在串口初始化函数中已经打开了总中断IT0 = 1;//外部中断的下降沿触发
}void main()
{Time0Init();//定时器0初始化UartInit();//串口相关初始化//外部中断初始化Ex0Init();while(1){if(signal){//定时器1s到点,把signal置一,主程序发送速度sprintf(speedMes,"speed:%d cm/s",speed);//串口数据的字符串拼装,speed是格子,每个格子1cmSendString(speedMes);//速度发出去signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一}}
}void speedHandler() interrupt 0 //外部中断处理函数
{speedCnt++;//码盘转动了一个格子
}//uart.c
#include "reg52.h"
#include "motor.h"
#include "string.h"
sbit D5 = P3^7;
#define SIZE 12sfr AUXR = 0x8E;
char buffer[SIZE];void UartInit(void) //9600bps@11.0592MHz
{AUXR = 0x01;SCON = 0x50; //配置串口工作方式1,REN使能接收TMOD &= 0x0F;TMOD |= 0x20;//定时器1工作方式位8位自动重装TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 1;//启动定时器EA = 1;//开启总中断ES = 1;//开启串口中断
}void SendByte(char mydata)
{SBUF = mydata;while(!TI);TI = 0;
}void SendString(char *str)
{while(*str != '\0'){SendByte(*str);str++;}
}//M1qian M2 hou M3 zuo M4 you
void Uart_Handler() interrupt 4
{static int i = 0;//静态变量,被初始化一次char tmp;if(RI)//中断处理函数中,对于接收中断的响应{RI = 0;//清除接收中断标志位tmp = SBUF;if(tmp == 'M'){i = 0;}buffer[i++] = tmp;//灯控指令if(buffer[0] == 'M'){switch(buffer[1]){case '1':goForward();break;case '2':goBack();break;case '3':goLeft();break;case '4':goRight();break;default:stop();break;}}if(i == 12) {memset(buffer, '\0', SIZE);i = 0;}}}//motor.c
#include "reg52.h"sbit RightCon1A = P3^7;
sbit RightCon1B = P3^3;sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;void goForward()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 1;
}void goRight()
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 0;
}void goLeft()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 1;
}void goBack()
{LeftCon1A = 1;LeftCon1B = 0;RightCon1A = 1;RightCon1B = 0;
}void stop()
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 0;
}//time.c
#include "motor.h"
#include "reg52.h"extern unsigned int speedCnt;
unsigned int speed;
char signal = 0;
unsigned int cnt = 0;void Time0Init()
{//1. 配置定时器0工作模式位16位计时TMOD = 0x01;//2. 给初值,定一个0.5出来TL0=0x33;TH0=0xFE;//3. 开始计时TR0 = 1;TF0 = 0;//4. 打开定时器0中断ET0 = 1;//5. 打开总中断EAEA = 1;
}void Time0Handler() interrupt 1
{cnt++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL0=0x33;TH0=0xFE;if(cnt == 2000){//爆表2000次,经过了1ssignal = 1;cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s//计算小车的速度,也就是拿到speedCnt的值speed = speedCnt;speedCnt = 0;//1秒后拿到speedCnt个格子,就能算出这1s的速度,格子清零}}
4. 小车速度显示在OLED屏
使用oled模块,oled写命令
写命令/数据的代码分析:
- /* 1. start()
- 2. 写入 b0111 1000 0x78
- 3. ACK
- 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
- 5. ACK
- 6. 写入指令/数据
- 7. ACK
- 8. STOP */
最终小车代码整合:
//main.c
#include "reg52.h"
#include "intrins.h"
#include "Oled.h"void main()
{//1. OLED初始化Oled_Init();Oled_Clear();Oled_Show_Str(2,2,"speed:35cm/s");while(1);
}//oled.c
#include "reg52.h"
#include "intrins.h"
#include "Oledfont.h"sbit scl = P1^2;
sbit sda = P1^3;void IIC_Start()
{scl = 0;sda = 1;scl = 1;_nop_();sda = 0;_nop_();
}void IIC_Stop()
{scl = 0;sda = 0;scl = 1;_nop_();sda = 1;_nop_();
}char IIC_ACK()
{char flag;sda = 1;//就在时钟脉冲9期间释放数据线_nop_();scl = 1;_nop_();flag = sda;_nop_();scl = 0;_nop_();return flag;
}void IIC_Send_Byte(char dataSend)
{int i;for(i = 0;i<8;i++){scl = 0;//scl拉低,让sda做好数据准备sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda_nop_();//发送数据建立时间scl = 1;//scl拉高开始发送_nop_();//数据发送时间scl = 0;//发送完毕拉低_nop_();//dataSend = dataSend << 1;}
}void Oled_Write_Cmd(char dataCmd)
{// 1. start()IIC_Start();// // 2. 写入从机地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据IIC_Send_Byte(0x00);// 5. ACKIIC_ACK();//6. 写入指令/数据IIC_Send_Byte(dataCmd);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Write_Data(char dataData)
{// 1. start()IIC_Start();// // 2. 写入从机地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据IIC_Send_Byte(0x40);// 5. ACKIIC_ACK();///6. 写入指令/数据IIC_Send_Byte(dataData);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Init(void){Oled_Write_Cmd(0xAE);//--display offOled_Write_Cmd(0x00);//---set low column addressOled_Write_Cmd(0x10);//---set high column addressOled_Write_Cmd(0x40);//--set start line address Oled_Write_Cmd(0xB0);//--set page addressOled_Write_Cmd(0x81); // contract controlOled_Write_Cmd(0xFF);//--128 Oled_Write_Cmd(0xA1);//set segment remap Oled_Write_Cmd(0xA6);//--normal / reverseOled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)Oled_Write_Cmd(0x3F);//--1/32 dutyOled_Write_Cmd(0xC8);//Com scan directionOled_Write_Cmd(0xD3);//-set display offsetOled_Write_Cmd(0x00);//Oled_Write_Cmd(0xD5);//set osc divisionOled_Write_Cmd(0x80);//Oled_Write_Cmd(0xD8);//set area color mode offOled_Write_Cmd(0x05);//Oled_Write_Cmd(0xD9);//Set Pre-Charge PeriodOled_Write_Cmd(0xF1);//Oled_Write_Cmd(0xDA);//set com pin configuartionOled_Write_Cmd(0x12);//Oled_Write_Cmd(0xDB);//set VcomhOled_Write_Cmd(0x30);//Oled_Write_Cmd(0x8D);//set charge pump enableOled_Write_Cmd(0x14);//Oled_Write_Cmd(0xAF);//--turn on oled panel
}void Oled_Clear()
{unsigned char i,j; //-128 --- 127for(i=0;i<8;i++){Oled_Write_Cmd(0xB0 + i);//page0--page7//每个page从0列Oled_Write_Cmd(0x00);Oled_Write_Cmd(0x10);//0到127列,依次写入0,每写入数据,列地址自动偏移for(j = 0;j<128;j++){Oled_Write_Data(0);}}
}void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2unsigned int i;Oled_Write_Cmd(0xb0+(row*2-2)); //page 0Oled_Write_Cmd(0x00+(col&0x0f)); //lowOled_Write_Cmd(0x10+(col>>4)); //high for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){Oled_Write_Data(F8X16[i]); //写数据oledTable1}Oled_Write_Cmd(0xb0+(row*2-1)); //page 1Oled_Write_Cmd(0x00+(col&0x0f)); //lowOled_Write_Cmd(0x10+(col>>4)); //highfor(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){Oled_Write_Data(F8X16[i]); //写数据oledTable1}
}/******************************************************************************/
// 函数名称:Oled_Show_Char
// 输入参数:oledChar
// 输出参数:无
// 函数功能:OLED显示单个字符
/******************************************************************************/
void Oled_Show_Str(char row,char col,char *str){while(*str!=0){Oled_Show_Char(row,col,*str);str++;col += 8; }
}