本项目基于stc89c52rc。pwm控速,语音控制切换 循迹 / 跟随 / 避障 / 蓝牙WIFI控制 等模式,包含测速屏显并传给上位机等功能。
目录
文章超详细,包含全部代码
一 L9110s电机驱动模块(后改为L1298N+18650电池x2)
控制电机动起来
二 小车动起来
1.控制小车前后左右停
2.无线射频遥控
三 蓝牙控制小车
1.串口
2.蓝牙控制
3.点动模式蓝牙控制
四 PWM调速
测试代码
左右轮分别调速
五 红外循迹、跟随
1.红外循迹模块
2.循迹原理
3.跟随
4.红外 循迹、跟随 代码整合
六 超声波避障
转头测距
代码:
结合小车运动实现避障
更改代码:
封装函数和改进
七 小车测速及OLED屏显
进行测速实验(发现问题与改进)
开始代码优化
蓝牙调速小车:
双轮测速
OLED显示速度
八 WIFI/4G通信
WiFi控制小车
Wifi传输速度
4G通信
九 SU-03T语音模块
十 功能整合——全能小车
全部代码:
主函数:
定时器及其中断
串口
红外循迹跟随
超声波避障
OLED显示
电机驱动
基于C51的智能小车
接通VCC,GND 模块电源指示灯亮, 以下资料来源官方,但是不对,根据下节课实际调试
IA1输入高电平,IA1输入低电平,【OA1 OB1】电机正转;
IA1输入低电平,IA1输入高电平,【OA1 OB1】电机反转;
IA2输入高电平,IA2输入低电平,【OA2 OB2】电机正转;
IA2输入低电平,IA2输入高电平,【OA2 OB2】电机反转;
和C51开发板的接线
接线中,单片机要单独接出一条gnd连接电源
控制轮子旋转,根据实际运行情况调整电机正反接线,保证两轮都向前
#include "reg52.h"
sbit RightA = P3^2;
sbit RightB = P3^3;
sbit LeftA = P3^4;
sbit LeftB = P3^5;
void main()
{
LeftA = 1;
LeftB = 0;
RightA = 1;
RightB = 0;
}
前后左右封装函数:
void forward(){
LeftA = 1;
LeftB = 0;
RightA = 1;
RightB = 0;
}
void back(){
LeftA = 0;
LeftB = 1;
RightA = 0;
RightB = 1;
}
void left(){
LeftA = 1;
LeftB = 1;
RightA = 1;
RightB = 0;
}
void right(){
LeftA = 1;
LeftB = 0;
RightA = 1;
RightB = 1;
}
void stop(){
LeftA = 1;
LeftB = 1;
RightA = 1;
RightB = 1;
}
void main()
{
while(1){
forward();
Delay2000ms();
back();
Delay2000ms();
left();
Delay2000ms();
right();
Delay2000ms();
stop();
Delay2000ms();
}
}
及其简单的IO操作,参考本人之前的文章,一看就会
(50条消息) C51:无线遥控电动车防盗器_我有在好好学习的博客-CSDN博客
点动和自锁模块都通用
#include "reg52.h"
#include "motor.h"
sbit D0 = P2^4;
sbit D1 = P2^5;
sbit D2 = P2^6;
sbit D3 = P2^7;
void main()
{
char dirc = 0;
while(1){
if(D0) dirc = 1;
else if(D1) dirc = 2;
else if(D2) dirc = 3;
else if(D3) dirc = 4;
else dirc = 0;
switch(dirc){
case 1:
forward();
break;
case 2:
back();
break;
case 3:
left();
break;
case 4:
right();
break;
case 0:
stop();
break;
}
}
}
后续为了不臃肿,分文件编程,参考上个c51温湿度监测系统项目最后的分文件操作:
(41条消息) 基于C51的 温湿度监测系统(及蓝牙温控风扇)_我有在好好学习的博客-CSDN博客
添加串口功能,用蓝牙进行控制,参考上面文章。
接收字符 f b l r s ,使得电机 前 后 左 右 停 。
main.c
#include "reg52.h"
#include "delay.h"
#include "motor.h"
#include "uart.h"
void main()
{
//串口初始化
UartInit();
while(1){
//给电机控制函数传入获得的串口指令
motor_ON(get_Uart());
}
}
motor.c中的函数
void motor_ON(char dir)
{
switch(dir){
case 'f':
forward();
break;
case 'b':
back();
break;
case 'l':
left();
break;
case 'r':
right();
break;
case 's':
stop();
break;
}
}
uart.c
默认电机状态为停 motor_dirc = 's'
#include "reg52.h"
sfr AUXR = 0x8E;
char motor_dirc = 's';
//串口部分代码
void UartInit(void) //[email protected]
{
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 data_msg)
{
SBUF = data_msg;
while(!TI);
TI = 0;
}
//发送字符串
void sendString(char* str)
{
while( *str != '\0'){
sendByte(*str);
str++;
}
}
//调取蓝牙命令内容
char get_Uart(){
return motor_dirc;
}
//蓝牙指令接收(中断)
void UART_HANDLER() interrupt 4
{
char cmd;
if(RI)//中断处理函数中,对于接收中断的响应
{
RI = 0;//清除接收中断标志位
cmd = SBUF;//接收命令
motor_dirc = cmd;
sendString("收到串口指令,改变状态\r\n");
}
}
给串口连接上蓝牙模块。
使用蓝牙串口助手,自定义设置好按键对应发送的字符,向设备发送命令。
最终实现了蓝牙控制小车方向。
蓝牙助手改为长按模式0.2s发一次指令
重点在于串口中断。
接收到指令也运动0.2s
//蓝牙指令接收(中断)
void UART_HANDLER() interrupt 4
{
char cmd;
if(RI)//中断处理函数中,对于接收中断的响应
{
RI = 0;//清除接收中断标志位
cmd = SBUF;//接收命令
motor_dirc = cmd;
switch(motor_dirc){
case 'f':
forward();
sendString("向前\r\n");
break;
case 'b':
back();
sendString("向后\r\n");
break;
case 'l':
left();
sendString("向左\r\n");
break;
case 'r':
right();
sendString("向右\r\n");
break;
case 's':
stop();
sendString("停止\r\n");
break;
}
}
//每次收到数据运动0.2秒
Delay200ms();
}
main.c 没收到指令就保持停滞状态。
void main()
{
//串口初始化
UartInit();
while(1){
//没收到指令就不动
stop();
}
}
参考本人之前的文章
(45条消息) C51:PWM+舵机角度控制_我有在好好学习的博客-CSDN博客
原理: 全速前进是 LeftA = 0; LeftB = 1; 完全停止是 LeftA = 0;LeftB = 0; 那么单位时间内,比如20ms, 有15ms是全速前进,5ms是完全停止,速度就会比5ms全速前进,15ms完全停止获得的功率多,相应的速度更快!
开发:借用PWM的舵机控制代码
#include "reg52.h"
#include "intrins.h"
sbit RightA = P3^2;
sbit RightB = P3^3;
sbit LeftA = P3^4;
sbit LeftB = P3^5;
int speed;//0~40
int cnt;
void Time0Init()//500微秒
{
//1. 配置定时器0工作模式位16位计时
TMOD = 0x01;
//2. 给初值,定一个10ms出来
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
//3. 开始计时,定时器"数数"
TR0 = 1;
TF0 = 0;
//4. 打开定时器0中断
ET0 = 1;
//5. 打开总中断EA
EA = 1;
}
void Delay2000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 15;
j = 2;
k = 235;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void forward(){
LeftA = 1;
LeftB = 0;
RightA = 1;
RightB = 0;
}
void stop(){
LeftA = 1;
LeftB = 1;
RightA = 1;
RightB = 1;
}
void main()
{
speed = 40;
cnt = 0;
Time0Init();
Delay2000ms();
while(1){
speed = 40;//快
Delay2000ms();
speed = 30;//中
Delay2000ms();
speed = 20;//慢
Delay2000ms();
speed = 10;//跑不动
Delay2000ms();
speed = 0;//停
Delay2000ms();
}
}
void Time0Handler() interrupt 1
{
cnt++; //统计爆表的次数
//重新给初值
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
//按照给定速度参数跑
if(cnt < speed){
forward();
}else{
stop();
}
if(cnt == 40){//爆表40次,经过了20ms
cnt = 0; //计算下一次的20ms
stop();
}
}
根据speed的大小测试对车速的影响,设置合适的速度
整合起来,分别通过两个 speed_Right、speed_Left 参数数值实现左右轮调速:
void PWM_Right(){
RightA = 1;
RightB = 0;
}
void PWM_Left(){
LeftA = 1;
LeftB = 0;
}
void PWM_STOP_Right(){
RightA = 1;
RightB = 1;
}
void PWM_STOP_Left(){
LeftA = 1;
LeftB = 1;
}
#include "reg52.h"
#include "motor.h"
int speed_Right = 40;//0~40
int cnt_Right =0;
int speed_Left = 40;//0~40
int cnt_Left =0;
//定时器0
void Time0Init()//500微秒
{
//1. 配置定时器0工作模式位16位计时
TMOD = 0x01;
//2. 给初值,定一个10ms出来
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
//3. 开始计时,定时器"数数"
TR0 = 1;
TF0 = 0;
//4. 打开定时器0中断
ET0 = 1;
//5. 打开总中断EA
EA = 1;
}
//定时器1
void Time1Init()//500微秒
{
//1. 配置定时器1工作模式位16位计时
TMOD &= 0x0F;
TMOD |= 0x1 << 4;
//2. 给初值,定一个10ms出来
TL1 = 0x33; //设置定时初值
TH1 = 0xFE; //设置定时初值
//3. 开始计时,定时器"数数"
TR1 = 1;
TF1 = 0;
//4. 打开定时器0中断
ET1 = 1;
//5. 打开总中断EA
EA = 1;
}
//右轮调速
void Time0Handler() interrupt 1
{
cnt_Right++; //统计爆表的次数
//重新给初值
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
//按照给定速度参数跑
if(cnt_Right < speed_Right){
PWM_Right();
}else{
PWM_STOP_Right();
}
if(cnt_Right == 40){//爆表40次,经过了20ms
cnt_Right = 0; //计算下一次的20ms
//stop();
}
}
//左轮调速
void Time1Handler() interrupt 3
{
cnt_Left++; //统计爆表的次数
//重新给初值
TL1 = 0x33; //设置定时初值
TH1 = 0xFE; //设置定时初值
//按照给定速度参数跑
if(cnt_Left < speed_Left){
PWM_Left();
}else{
PWM_STOP_Left();
}
if(cnt_Left == 40){//爆表40次,经过了20ms
cnt_Left = 0; //计算下一次的20ms
}
}
#include "reg52.h"
#include "delay.h"
#include "motor.h"
#include "uart.h"
#include "pwm.h"
extern char motor_dirc;
void main()
{
//串口初始化
UartInit();
//定时器开启
Time0Init();
Time1Init();
while(1){
motor_dirc = SBUF;
}
}
TCRT5000传感器的红外发射二极管不断发射红外线
当发射出的红外线没有被反射回来或被反射回来但强度不够大时,
红外接收管一直处于关断状态,此时模块的输出端为高电平,指示二极管一直处于熄灭状态
被检测物体出现在检测范围内时,红外线被反射回来且强度足够大,红外接收管饱和,
此时模块的输出端为低电平,指示二极管被点亮
总结就是一句话,没反射回来,D0输出高电平,灭灯
接线方式
VCC:接电源正极(3-5V)
GND:接电源负极
DO:TTL开关信号输出0、1
AO:模拟信号输出(不同距离输出不同的电压,此脚一般可以不接)
由于黑色具有较强的吸收能力,当循迹模块发射的红外线照射到黑线时,红外线将会被黑线吸收,导致循迹模块上光敏三极管处于关闭状态,此时模块上一个LED熄灭。在没有检测到黑线时,模块上两个LED常亮
总结就是一句话,有感应到黑线,D0输出高电平 ,灭灯
循迹模块安装在小车车头两侧
下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走
上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转
原理和寻线是一样的,寻线红外观朝下,跟随朝前
左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
#include "reg52.h"
#include "delay.h"
sbit Finder_Left = P2^0;
sbit Finder_Right = P2^1;
sbit Flow_Left = P2^3;
sbit Flow_Right = P2^2;
extern char speed_Right;//0~40
extern char speed_Left;//0~40
//寻线功能
void checkWay(){
//左边触线,左转
if(Finder_Left && !Finder_Right){
speed_Right = 40;
speed_Left = 10;
}
//右边触线,左转
else if(Finder_Right && !Finder_Left){
speed_Right = 7;
speed_Left = 40;
}
//车悬空,停止移动
else if(Finder_Right && Finder_Left){
speed_Right = 0;
speed_Left = 0;
}
//直线,直行
else{
speed_Right = 36;
speed_Left = 36;
}
}
//跟随功能
void flow(){
//左边有人,左转
if(Flow_Right && !Flow_Left){
speed_Right = 40;
speed_Left = 5;
}
//右边有人,右转
else if(Flow_Left && !Flow_Right){
speed_Right = 5;
speed_Left = 40;
}
//车悬空,停止移动
else if(Flow_Right && Flow_Left){
speed_Right = 0;
speed_Left = 0;
}
//人在前方,直行
else{
speed_Right = 40;
speed_Left = 40;
}
}
使用超声波模块测距+舵机模块左右摇头实现发现障碍物和寻找出路
首先写一段超声波测距,前方距离小于30cm就左右摇头测左右的距离
#include "reg52.h"
#include "pwm.h"
#include "delay.h"
extern char angle;
void main()
{
double disMiddle;
double disLeft;
double disRight;
//定时器开启
Time0Init();
Time1Init();
angle = 3;
Delay300ms();
while(1){
if(get_distance() < 30){
//角度0 左
angle = 1;
disLeft = get_distance();
Delay300ms();
//角度90 前
angle = 3;
disMiddle = get_distance();
Delay300ms();
//角度180 右
angle = 5;
disRight = get_distance();
Delay300ms();
//回到90度向前
angle = 3;
Delay300ms();
}
}
}
#include "reg52.h"
#include "motor.h"
#include "delay.h"
char cnt_Right =0;
//舵机引脚
sbit sg90_con = P1^1;
//超声波
sbit Trig = P1^5;
sbit Echo = P1^6;
char angle;
//定时器0
void Time0Init()//500微秒
{
//1. 配置定时器0工作模式位16位计时
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01;
//2. 给初值,定一个0.5出来
TL0=0x33;
TH0=0xFE;
//3. 开始计时
TR0 = 1;
TF0 = 0;
//4. 打开定时器0中断
ET0 = 1;
//5. 打开总中断EA
EA = 1;
}
void Time1Init(void)//测距使用;T1
{
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x10;
TH1 = 0;
TL1 = 0;
//设置定时器0工作模式1,初始值设定0开始数数,不着急启动定时器
}
void startHC(){//发出超声波
Trig = 0;
Trig = 1;
Delay10us();
Trig = 0;
}
//测距功能:
double get_distance()
{
double time;
//定时器数据清零,以便下一次测距
TH1 = 0;
TL1 = 0;
//1. Trig ,给Trig端口至少10us的高电平
startHC();
//2. echo由低电平跳转到高电平,表示开始发送波
while(Echo == 0);
//波发出去的那一下,开始启动定时器
TR1 = 1;
//3. 由高电平跳转回低电平,表示波回来了
while(Echo == 1);
//波回来的那一下,我们开始停止定时器
TR1 = 0;
//4. 计算出中间经过多少时间
time = (TH1 * 256 + TL1)*1.085;//us为单位
//5. 距离 = 速度 (340m/s)* 时间/2
return (time * 0.017);
}
void Time0Handler() interrupt 1
{
cnt_Right++; //统计爆表的次数
//重新给初值
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
if(cnt_Right < angle){
sg90_con = 1;
}else{
sg90_con = 0;
}
if(cnt_Right == 40){//爆表40次,经过了20ms
cnt_Right = 0; //计算下一次的20ms
sg90_con = 1;
}
}
摇头测距哪边更宽阔就往哪边转
更改while循环中的代码,即可完成超声波避障
//距离过近,后退
if(get_distance() < 10){
back();
Delay100ms();
}
//距离小于30,测距转向
else if(get_distance() < 30){
//距离小于30,停车
stop();
//摇头测距
angle = 1;//角度0 右
disRight = get_distance();
Delay300ms();
angle = 3;//角度90 前
disMiddle = get_distance();
Delay300ms();
angle = 5;//角度180 左
disLeft = get_distance();
Delay300ms();
angle = 3;//回到90度向前
Delay300ms();
//根据测距判断左右转
if(disLeft >= disRight){
left();
Delay100ms();
}else if(disLeft < disRight){
right();
Delay100ms();
}
}
//安全距离,前进
else{
forward();
}
调整了小车的过近后退距离阈值,另外小车有时会在一个地方一直左右转头,用一个标志位稍作调整。
#include "reg52.h"
#include "delay.h"
#include "motor.h"
#include "pwm.h"
double disMiddle;
double disLeft;
double disRight;
extern char angle;
//防止一直原地左右摆头
char same_dirc_flag = 0;
void evadible()
{
//距离过近,后退
if(get_distance() < 15){
back();
same_dirc_flag = 0;
Delay100ms();
}
//距离小于30,测距转向
else if(get_distance() < 30){
//距离小于30,停
stop();
//摇头测距
angle = 1;//角度0 右
disRight = get_distance();
Delay300ms();
angle = 3;//角度90 前
disMiddle = get_distance();
Delay300ms();
angle = 5;//角度180 左
disLeft = get_distance();
Delay300ms();
angle = 3;//回到90度向前
Delay300ms();
//根据测距判断左右转,避免原地左右转
if(disLeft >= disRight && same_dirc_flag != 'r'){
left();
same_dirc_flag = 'l';
Delay100ms();
}else if(disLeft < disRight && same_dirc_flag != 'l'){
right();
same_dirc_flag = 'r';
Delay100ms();
}else if(same_dirc_flag == 'l'){
left();
same_dirc_flag = 'r';
Delay100ms();
}else if(same_dirc_flag == 'r'){
right();
same_dirc_flag = 'r';
Delay100ms();
}
}
//安全距离,前进
else{
forward();
same_dirc_flag = 0;
}
}
用途:广泛用于电机转速检测,脉冲计数,位置限位等。
有遮挡,输出高电平;无遮挡,输出低电平
接线:P3.2 P3.3 为外部中断
VCC 接电源正极3.3-5V
GND 接电源负极
DO TTL开关信号输出
AO 此模块不起作用
测速原理和单位换算
轮子走一圈,经过一个周长,C = 2x3.14x半径= 3.14 x 直径(6.5cm)
对应的码盘也转了一圈,码盘有20个格子,每经过一个格子,会遮挡(高电平)和不遮挡(低电平),
那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM
定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm
假设一秒有80脉冲,那么就是80cm/s
利用外部中断,计时器计时1秒,测算传感器被激发了几次。
但是由于代码写的太屎山,光是计时就已经分别用掉了两个计时器,此时串口就没有计时器可用了。
所以可以看出,c51资源很珍贵,我们在做项目时一定要节约资源。
本项目主控作为只有8位的c51,资源是及其有限的,节约资源也就成l51开发的重点,不然是无法
完成一个出色的功能复杂的项目的。
代码优化空间很大,例如pwm调速,没必要使用双计时器分别调速,一个计时器即可完成调速。
于是优化了之前的代码:
定时器pwm调速部分
#include "reg52.h"
#include "motor.h"
char speed_Right = 0;//0~40
char speed_Left = 0;//0~40
char cnt =0;
//定时器0
void Time0Init()//500微秒
{
//1. 配置定时器0工作模式位16位计时
TMOD = 0x01;
//2. 给初值,定一个10ms出来
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
//3. 开始计时,定时器"数数"
TR0 = 1;
TF0 = 0;
//4. 打开定时器0中断
ET0 = 1;
//5. 打开总中断EA
EA = 1;
}
//双轮调速
void Time0Handler() interrupt 1
{
cnt++; //统计爆表的次数
//重新给初值
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
//按照给定速度参数跑:右轮
if(cnt < speed_Right){
PWM_Right();
}else{
PWM_STOP_Right();
}
//按照给定速度参数跑:左轮
if(cnt < speed_Left){
PWM_Left();
}else{
PWM_STOP_Left();
}
if(cnt == 40){//爆表40次,经过了20ms
cnt = 0; //计算下一次的20ms
//stop();
}
}
串口中断部分,实现了使用蓝牙遥控调速,速度串口传输显示,这是很方便调参的方法。
//蓝牙指令接收(中断)
void UART_HANDLER() interrupt 4
{
char cmd;
if(RI)//中断处理函数中,对于接收中断的响应
{
RI = 0;//清除接收中断标志位
cmd = SBUF;//接收命令
motor_dirc = cmd;
switch(motor_dirc){
case 'f':
speed_Right = high;
speed_Left = high;
sendString("向前\r\n");
break;
case 'l':
speed_Right = high;
speed_Left = low;
sendString("向左\r\n");
break;
case 'r':
speed_Right = low;
speed_Left = high;
sendString("向右\r\n");
break;
case 'u':
if(high < 40){
high += 5;
sendString("加速,当前速度:");
sendByte(high/10 + 0x30);
sendByte(high%10 + 0x30);
sendString("\r\n");
speed_Right = high;
speed_Left = high;
}
else{
sendString("已达到最高速:");
sendByte(high/10 + 0x30);
sendByte(high%10 + 0x30);
sendString("\r\n");
}
break;
case 'd':
if(high > 10){
high -= 5;
sendString("减速,当前速度:");
sendByte(high/10 + 0x30);
sendByte(high%10 + 0x30);
sendString("\r\n");
speed_Right = high;
speed_Left = high;
}
else{
sendString("已达到最低速:");
sendByte(high/10 + 0x30);
sendByte(high%10 + 0x30);
sendString("\r\n");
}
break;
case 's':
speed_Right = 0;
speed_Left = 0;
sendString("向前\r\n");
break;
}
}
//Delay200ms();
//speed_Right = 0;
//speed_Left = 0;
}
轻松搞定,实时显示设置的占空比
同理我们可以优化测速代码实现双轮测速
#include "reg52.h"
#include "motor.h"
int cnt = 0;
int Check_speed_Right = 0;//cm/s
int speed_Right_cm_s = 0;//cm/s
int Check_speed_Left = 0;//cm/s
int speed_Left_cm_s = 0;//cm/s
char signal = 0;
//定时器0
void Time0Init()//500微秒
{
//1. 配置定时器0工作模式位16位计时
TMOD = 0x01;
//2. 给初值,定一个10ms出来
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
//3. 开始计时,定时器"数数"
TR0 = 1;
TF0 = 0;
//4. 打开定时器0中断
ET0 = 1;
//5. 打开总中断EA
EA = 1;
}
//右轮外部中断
void Left_EX0_init(){
//打开外部中断
EX0 = 1;
//下降沿触发
IT0 = 1;
}
//左轮外部中断
void Right_EX1_init(){
//打开外部中断
EX1 = 1;
//下降沿触发
IT1 = 1;
}
//右轮外部中断,计数
void EX0_handler() interrupt 0
{
Check_speed_Right++;
}
//左轮外部中断,计数
void EX1_handler() interrupt 2
{
Check_speed_Left++;
}
//双轮测速
void Time0Handler() interrupt 1
{
cnt++; //统计爆表的次数
//重新给初值
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
//测速
if(cnt == 2000){//爆表2000次,经过了1s
speed_Right_cm_s = Check_speed_Right;//取得右轮速度数值
Check_speed_Right = 0;//每秒归零
speed_Left_cm_s = Check_speed_Left;//取得左轮速度数值
Check_speed_Left = 0;//每秒归零
signal = 1;//信号告知main函数接收数据
cnt = 0; //计算下一次的1s
}
}
传输字符串,数据类型不同,所以可以拼接一个字符串
sprintf();
#include "reg52.h"
#include "uart.h"
#include "timer.h"
#include "stdio.h"
extern int speed_Right_cm_s;
extern int speed_Left_cm_s;
extern char signal;
int speed;
char speedMes[24]; //主程序发送速度数据的字符串缓冲区
void main()
{
Time0Init();
Left_EX0_init();
Right_EX1_init();
//串口初始化
UartInit();
while(1){
if(signal)
{
speed = (speed_Right_cm_s + speed_Left_cm_s)/2;
//串口数据的字符串拼装,speed是格子,每个格子1cm
sprintf(speedMes,"Speed: %d ; R: %d ; L: %d",speed,speed_Right_cm_s,speed_Left_cm_s);
sendString(speedMes);//速度发出去
sendString("\r\n");
signal = 0;
}
}
}
轻松搞定双轮速度,平均速度
参考本人上一篇文章
(48条消息) C51:IIC_OLED_我有在好好学习的博客-CSDN博客
这里开始引入官方提供的字库文档。
//========================官方提供的代码================================
void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
unsigned int i;
Oled_Write_Cmd(0xb0+(row*2-2)); //page 0
Oled_Write_Cmd(0x00+(col&0x0f)); //low
Oled_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 1
Oled_Write_Cmd(0x00+(col&0x0f)); //low
Oled_Write_Cmd(0x10+(col>>4)); //high
for(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;
}
}
//================================================================================
#include "reg52.h"
#include "timer.h"
#include "stdio.h"
#include "oled.h"
extern int speed_Right_cm_s;
extern int speed_Left_cm_s;
extern char signal;
int speed;
char speedMes[24]; //主程序发送速度数据的字符串缓冲区
char speedMes1[24];
char speedMes2[24];
void main()
{
Time0Init();
Left_EX0_init();
Right_EX1_init();
//串口初始化
UartInit();
Oled_Init();
while(1){
if(signal)
{
speed = (speed_Right_cm_s + speed_Left_cm_s)/2;
//串口数据的字符串拼装,speed是格子,每个格子1cm
sprintf(speedMes,"Speed: %d cm/s",speed);
sprintf(speedMes1,"Right: %d cm/s",speed_Right_cm_s);
sprintf(speedMes2,"Left : %d cm/s",speed_Left_cm_s);
Oled_Show_Str(1,10,speedMes);
Oled_Show_Str(2,10,speedMes1);
Oled_Show_Str(3,10,speedMes2);
signal = 0;
}
}
}
同时用蓝牙控制运动,运行效果:
模块为ESP8266
在蓝牙串口控制小车的代码进行修改,蓝牙因为本身就透传,所以比较简单。
这里需要把esp8266设置为服务器。
参考本人文章:
(49条消息) C51:蓝牙/Wifi/4G 无线控制开关_我有在好好学习的博客-CSDN博客
#include "reg52.h"
#include "delay.h"
#include "uart.h"
#include "wifi.h"
code char CIPSEND[] = "AT+CIPSEND=0,8\r\n";// 发送8个字节在连接0通道上
void main()
{
//串口初始化
UartInit();
BuildWifi();
while(1){
sendString(CIPSEND);
Delay1000ms();
sendString("---52---");
Delay1000ms();
}
}
新建一个wifi分文件,给模块发送AT指令。
用一些标志位保证连接过程的正常进行,用LED灯充当WIFI状态指示灯方便观察。
#include "reg52.h"
#include
#include "uart.h"
#include "delay.h"
//========wifi=========
sbit D5 = P3^7;
sbit D6 = P3^6;//WIFI状态指示灯
//1 配置成双模
code char CWMODE[] = "AT+CWMODE=2\r\n";
//2 使能多链接
code char CIPMUX[] = "AT+CIPMUX=1\r\n";
//3 建立TCPServer
code char CIPSERVER[] = "AT+CIPSERVER=1\r\n";// default port = 333
char OK_FLAG = 0;
char Connect_Mark = 1;
char Client_Mark = 0;
//==========创建wifi服务器==================
void BuildWifi()
{
while(1){
Delay1000ms();//给上电时间
//闪一下
D6 = 0;
Delay200ms();
D6 = 1;
//1 配置成双模
sendString(CWMODE);
//判断是否OK
while(!OK_FLAG)
{
//判断如果ERROR,跳出循环
if(!Connect_Mark) break;
}
//复位OK标志位
OK_FLAG = 0;
//ERROR则重新开始连接
if(!Connect_Mark){
Connect_Mark = 1;
continue;
}
//闪一下
D6 = 0;
Delay200ms();
D6 = 1;
//2 使能多链接
sendString(CIPMUX);
//判断是否OK
while(!OK_FLAG)
{
//判断如果ERROR,跳出循环
if(!Connect_Mark) break;
}
//复位OK标志位
OK_FLAG = 0;
//ERROR则重新开始连接
if(!Connect_Mark){
Connect_Mark = 1;
continue;
}
//闪一下
D6 = 0;
Delay200ms();
D6 = 1;
//3 建立TCPServer
sendString(CIPSERVER);
//判断是否OK
while(!OK_FLAG)
{
//判断如果ERROR,跳出循环
if(!Connect_Mark) break;
}
//复位OK标志位
OK_FLAG = 0;
//ERROR则重新开始连接
if(!Connect_Mark){
Connect_Mark = 1;
continue;
}
//完成退出循环
break;
}
D6 = 0;//点亮D6,代表连接服务器并打开透传模式成功
while(!Client_Mark);
D5 = 0;//点亮D6,代表用户连接
}
串行中断的代码进行扩充,以适应与Wifi模块的通信。
#include "reg52.h"
#include "motor.h"
#include "delay.h"
#include "wifi.h"
#include
sbit D5 = P3^7;
sbit D6 = P3^6;//WIFI状态指示灯
sfr AUXR = 0x8E;
char cmd;
char buffer[12];
extern char OK_FLAG;
extern char Connect_Mark;
extern char Client_Mark;
char motor_dirc = 's';
//..............
//串口指令接收(中断)
void UART_HANDLER() interrupt 4
{
static int i = 0;
if(RI){
RI = 0;
cmd = SBUF;
if(cmd == 'O' || cmd == 'E' || cmd == '0' || cmd == ':' || cmd == 'M'){
i = 0;
}
buffer[i++] = cmd;
//连接服务器等OK返回值指令的判断
if(buffer[0] == 'O' && buffer[1] == 'K'){
OK_FLAG = 1;
memset(buffer, '\0', 12);
}
//失败持续闪灯4秒,然后重启
if(buffer[0] == 'E' && buffer[1] == 'R' && buffer[2] == 'R'){
for(i = 0;i<10;i++){
Delay200ms();
D6 = 1;
Delay200ms();
D6 = 0;
}
//改变连接标志位
Connect_Mark = 0;
i = 0;
memset(buffer, '\0', 12);
}
//用户连接与否判断
if(buffer[0] == '0' && buffer[2] == 'C'){
Client_Mark = 1;
memset(buffer, '\0', 12);
}
//开关灯指令
if(buffer[0] == ':' && buffer[1] == 'o' && buffer[2] == 'p'){
D5 = 0;
memset(buffer, '\0', 12);
}
else if(buffer[0] == ':' && buffer[1] == 'c' && buffer[2] == 'l'){
D5 = 1;
memset(buffer, '\0', 12);
}
if(buffer[0] == 'M')
{
motor_dirc = buffer[1];
switch(motor_dirc){
case 'f':
forward();
memset(buffer, '\0', 12);
break;
case 'b':
back();
memset(buffer, '\0', 12);
break;
case 'l':
left();
memset(buffer, '\0', 12);
break;
case 'r':
right();
memset(buffer, '\0', 12);
break;
case 's':
stop();
memset(buffer, '\0', 12);
break;
}
}
if(i == 12)
{
i = 0;
memset(buffer, '\0', 12);
}
}
}
结合Oled显示的代码进行扩展,此时变量会很多很大,要节约内存防止编译不通过。
代码基本区别不大,注意细节调整,比如CIPSEND的大小。
#include "reg52.h"
#include "timer.h"
#include "stdio.h"
#include "oled.h"
#include "uart.h"
#include "WiFi.h"
extern int speed_Right;
extern int speed_Left;
extern char signal;
int speed;
char speedMes[14]; //主程序发送速度数据的字符串缓冲区
char speedMes1[14];
char speedMes2[13];
//准备Wifi传输
extern code char CIPSEND[];
void main()
{
Time0Init();
Left_EX0_init();
Right_EX1_init();
//串口初始化
UartInit();
BuildWifi();
Oled_Init();
Oled_Clear();
while(1){
if(signal)
{
speed = (speed_Right + speed_Left)/2;
//串口数据的字符串拼装,speed是格子,每个格子1cm
sprintf(speedMes,"Speed: %d cm/s",speed);
sprintf(speedMes1,"Right: %d cm/s",speed_Right);
sprintf(speedMes2,"Left : %d cm/s",speed_Left);
//oled屏显
Oled_Show_Str(1,10,speedMes);
Oled_Show_Str(2,10,speedMes1);
Oled_Show_Str(3,10,speedMes2);
//wifi传输
sendString(speedMes);
//标志位归0
signal = 0;
//准备下一次wifi传输
sendString(CIPSEND);
}
}
}
代码不增反减,配置依然可以参考我之前的文章。做好外网透传就好了。
(50条消息) C51:蓝牙/Wifi/4G 无线控制开关_我有在好好学习的博客-CSDN博客
这里就不做过多赘述。
SU-03T LD3320,都是常见的非特定人语音识别模块,这里选用无需二次开发的SU-03T,用官方网站可以快速配置SDK。
网盘资料
https://pan.baidu.com/s/1dXQSla_8D5O3D7UCsHZ0Dw 提取码3k5a
在官方网站可以快速配置,等待生成SDK
下载SDK
烧录完成就可以使用了
整合循迹、避障、跟随、串口控制功能。串口选用蓝牙即可,比WiFi和4g更稳定好用。
比较考验综合能力,难点在于在定时器0的中断上添加大量代码时,如何平衡性能。一个中断上的大量代码可能会使得单片机吃不消,所以要判断不同模式下功能的切换和取舍。并且定时器1为串口使用,避障模式需要0和1双定时器,这时就要判断切换定时器1的使用状态。
少量如软件延时,就不细写了。
#include "reg52.h"
#include "timer.h"
#include "oled.h"
#include "uart.h"
#include "find.h"
#include "evadible.h"
#include "delay.h"
#include "motor.h"
//蓝牙、循迹、跟随、避障
#define UART 0
#define XunJi 1
#define GenSui 2
#define BiZhang 3
//语音模块接口
sbit A25 = P2^5;
sbit A26 = P2^6;
sbit A27 = P2^7;
//默认小车串口模式,定时器1供串口使用
char mode = UART;
char timer1_mark = UART;
//是否需要开启pwm
extern char pwm_flag;
//==========主函数==========
void main()
{
//定时器0初始化(pwm调速及测速)
Time0Init();
//外部中断初始化(测速)
Left_EX0_init();
Right_EX1_init();
//串口初始化
UartInit();
//Oled初始化并清屏
Oled_Init();
Oled_Clear();
//一直循环判断
while(1){
//判断语音模块的输入结果
if(!A25) mode = XunJi;
else if(!A26) mode = BiZhang;
else if(!A27) mode = GenSui;
else mode = UART;
//选择工作模式
switch(mode){
//======蓝牙(串口)模式================
case UART:
//判断定时器1状态和是否切换给串口使用
if(timer1_mark != UART){
stop();
UartInit();
}
timer1_mark = UART;
//默认不使用pwm调速为蓝牙模式
pwm_flag = 0;
break;
//=======循迹模式======================
case XunJi:
//判断定时器1状态和是否切换给串口使用
if(timer1_mark != UART){
stop();
UartInit();
}
timer1_mark = UART;
//使用PWM调速,红外循迹
pwm_flag = 1;
checkWay();
break;
//=======跟随模式======================
case GenSui:
//判断定时器1状态和是否切换给串口使用
if(timer1_mark != UART){
stop();
UartInit();
}
timer1_mark = UART;
//使用PWM调速,红外跟随
pwm_flag = 1;
follow();
break;
//=======避障模式======================
case BiZhang:
//将定时器1用来测距
if(timer1_mark != BiZhang){
stop();
TR1 = 0;
Time1Init();
}
timer1_mark = BiZhang;
//不使用PWM调速,也不输出测速结果
pwm_flag = 2;
evadible();//超声波避障
break;
}
}
}
#include "reg52.h"
#include "motor.h"
#include "uart.h"
#include "stdio.h"
#include "oled.h"
#include "delay.h"
//舵机引脚
sbit sg90_con = P1^1;
//超声波
sbit Trig = P1^2;
sbit Echo = P1^3;
//舵机角度
char angle = 3;
//计时次数标记
int cnt = 0;
int cnt_pwm = 0;
//累计每秒脉冲次数(测速)
int Check_speed_Right = 0;//cm/s
int Check_speed_Left = 0;//cm/s
//传输显示的速度值(测速)
int speed_Right_cm_s = 0;//cm/s
int speed_Left_cm_s = 0;//cm/s
//pwm调速标志位及调速参数
char pwm_flag = 0;
char speed_Right = 0;//0~40
char speed_Left = 0;//0~40
//两轮平均速度
int speed;
//速度信息字符串
char speedMes[14]; //主程序发送速度数据的字符串缓冲区
char speedMes1[14];
char speedMes2[13];
//定时器0
void Time0Init()//500微秒
{
//1. 配置定时器0工作模式位16位计时
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01;
//2. 给初值,定一个0.5出来
TL0=0x33;
TH0=0xFE;
//3. 开始计时
TR0 = 1;
TF0 = 0;
//4. 打开定时器0中断
ET0 = 1;
//5. 打开总中断EA
EA = 1;
}
//==========超声波测距==========
//测距使用;T1
void Time1Init(void)
{
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x10;
TH1 = 0;
TL1 = 0;
//设置定时器0工作模式1,初始值设定0开始数数,不着急启动定时器
}
//发出超声波
void startHC(){
Trig = 0;
Trig = 1;
Delay10us();
Trig = 0;
}
//测距功能:
double get_distance()
{
double time;
//定时器数据清零,以便下一次测距
TH1 = 0;
TL1 = 0;
//1. Trig ,给Trig端口至少10us的高电平
startHC();
//2. echo由低电平跳转到高电平,表示开始发送波
while(Echo == 0);
//波发出去的那一下,开始启动定时器
TR1 = 1;
//3. 由高电平跳转回低电平,表示波回来了
while(Echo == 1);
//波回来的那一下,我们开始停止定时器
TR1 = 0;
//4. 计算出中间经过多少时间
time = (TH1 * 256 + TL1)*1.085;//us为单位
//5. 距离 = 速度 (340m/s)* 时间/2
return (time * 0.017);
}
//===========外部中断测速==============
//右轮外部中断
void Left_EX0_init(){
//打开外部中断
EX0 = 1;
//下降沿触发
IT0 = 1;
}
//左轮外部中断
void Right_EX1_init(){
//打开外部中断
EX1 = 1;
//下降沿触发
IT1 = 1;
}
//右轮外部中断,计数
void EX0_handler() interrupt 0
{
Check_speed_Right++;
}
//左轮外部中断,计数
void EX1_handler() interrupt 2
{
Check_speed_Left++;
}
//========舵机转动、双轮调速、测速(中断)========
void Time0Handler() interrupt 1
{
cnt++; //统计爆表的次数
cnt_pwm++;
//重新给初值
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
//舵机pwm转动
if(cnt_pwm < angle){
sg90_con = 1;
}else{
sg90_con = 0;
}
//pwm调速
if(pwm_flag == 1)
{
//按照给定速度参数跑:右轮
if(cnt_pwm < speed_Right){
PWM_Right();
}else{
PWM_STOP_Right();
}
//按照给定速度参数跑:左轮
if(cnt_pwm < speed_Left){
PWM_Left();
}else{
PWM_STOP_Left();
}
}
if(cnt_pwm == 40){//开始下一次的20ms
cnt_pwm = 0;
sg90_con = 1;
}
//测速并传输显示
if(cnt == 2000){//爆表2000次,经过了1s
//右测速
speed_Right_cm_s = Check_speed_Right;//取得右轮速度数值
Check_speed_Right = 0;//每秒归零
//左测速
speed_Left_cm_s = Check_speed_Left;//取得左轮速度数值
Check_speed_Left = 0;//每秒归零
if(!pwm_flag)//pwm调速或测距时,不测速Oled显示。因为输出信息量太大,影响pwm和测距。
{
//计算综合速度
speed = (speed_Right_cm_s + speed_Left_cm_s)/2;
//串口数据的字符串拼装,speed是格子,每个格子1cm
sprintf(speedMes,"Speed: %d cm/s",speed);
sprintf(speedMes1,"Right: %d cm/s",speed_Right_cm_s);
sprintf(speedMes2,"Left : %d cm/s",speed_Left_cm_s);
//蓝牙传输速度信息
sendString(speedMes);
sendString("\r\n");
//Oled显示速度和左右分别速度
Oled_Show_Str(1,10,speedMes);
Oled_Show_Str(2,10,speedMes1);
Oled_Show_Str(3,10,speedMes2);
}
//开始下一次的1s
cnt = 0;
}
}
#include "reg52.h"
#include "motor.h"
#include "delay.h"
sfr AUXR = 0x8E;
char motor_dirc = 's';
extern char pwm_flag;
//串口部分代码
void UartInit(void) //[email protected]
{
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 data_msg)
{
SBUF = data_msg;
while(!TI);
TI = 0;
}
//发送字符串
void sendString(char* str)
{
while( *str != '\0'){
sendByte(*str);
str++;
}
}
//蓝牙指令接收(中断)
void UART_HANDLER() interrupt 4
{
char cmd;
if(RI)//中断处理函数中,对于接收中断的响应
{
RI = 0;//清除接收中断标志位
cmd = SBUF;//接收命令
//根据命令运动
motor_dirc = cmd;
if(!pwm_flag)
{
switch(motor_dirc){
case 'f':
forward();
break;
case 'b':
back();
break;
case 'l':
left();
break;
case 'r':
right();
break;
case 's':
stop();
break;
}
//每次收到命令运动0.2秒,不然就停,模拟点动模式
Delay200ms();
stop();
}
}
}
#include "reg52.h"
#include "delay.h"
sbit Finder_Left = P2^0;
sbit Finder_Right = P2^1;
sbit Flow_Left = P2^2;
sbit Flow_Right = P2^3;
extern char speed_Right;//0~40
extern char speed_Left;//0~40
//寻线功能
void checkWay(){
//左边触线,左转
if(Finder_Left && !Finder_Right){
speed_Right = 37;
speed_Left = 1;
}
//右边触线,左转
else if(Finder_Right && !Finder_Left){
speed_Right = 1;
speed_Left = 36;
}
//车悬空,停止移动
else if(Finder_Right && Finder_Left){
speed_Right = 0;
speed_Left = 0;
}
//直线,直行
else{
speed_Right = 18;
speed_Left = 17;
}
}
//跟随功能
void follow(){
//左边有人,左转
if(Flow_Right && !Flow_Left){
speed_Right = 37;
speed_Left = 1;
}
//右边有人,右转
else if(Flow_Left && !Flow_Right){
speed_Right = 1;
speed_Left = 36;
}
//车悬空,停止移动
else if(Flow_Right && Flow_Left){
speed_Right = 0;
speed_Left = 0;
}
//人在前方,直行
else{
speed_Right = 19;
speed_Left = 18;
}
}
#include "reg52.h"
#include "delay.h"
#include "motor.h"
#include "timer.h"
double disMiddle;
double disLeft;
double disRight;
extern char angle;
//防止一直原地左右摆头
char same_dirc_flag = 0;
void evadible()
{
//距离过近,后退
if(get_distance() < 15){
back();
Delay100ms();
}
//距离小于30,测距转向
else if(get_distance() < 30){
//距离小于30,停
stop();
//摇头测距
angle = 1;//角度0 右
disRight = get_distance();
Delay300ms();
angle = 3;//角度90 前
disMiddle = get_distance();
Delay300ms();
angle = 5;//角度180 左
disLeft = get_distance();
Delay300ms();
angle = 3;//回到90度向前
Delay100ms();
//根据测距判断左右转,避免原地左右转
if(disLeft >= disRight && same_dirc_flag != 'r'){
left();
same_dirc_flag = 'l';
Delay100ms();
}else if(disLeft < disRight && same_dirc_flag != 'l'){
right();
same_dirc_flag = 'r';
Delay100ms();
}else if(same_dirc_flag == 'l'){
left();
same_dirc_flag = 'r';
Delay100ms();
}else if(same_dirc_flag == 'r'){
right();
same_dirc_flag = 'r';
Delay100ms();
}
}
//安全距离,前进
else{
forward();
same_dirc_flag = 0;
}
}
#include "reg52.h"
#include
#include "oledfont.h"
sbit SCL = P0^1;
sbit SDA = P0^3;
//开始信号
void IIC_START()
{
SCL = 0;//防止雪花
SDA = 1;
SCL = 1;
_nop_();
SDA = 0;
_nop_();
}
//结束信号
void IIC_END()
{
SCL = 0;//防止雪花
SDA = 0;
SCL = 1;
_nop_();
SDA = 1;
_nop_();
}
//应答信号
char IIC_ACK()
{
char flag;
SDA = 1;
_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;
SDA = dataSend & 0x80;//1000 0000 获得dataSend最高位给SDA
_nop_();//数据建立时间
SCL = 1;//SCL拉高开始发送
_nop_();//数据发送时间
SCL = 0;//发送完拉低
_nop_();
dataSend <<= 1;
}
}
//写入指令
void Oled_Write_Cmd(char OLED_cmd)
{
// 1. start()
IIC_START();
// 2. 写入从机地址 b0111 1000 0x78
IIC_Send_Byte(0x78);
// 3. ACK
IIC_ACK();
// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
IIC_Send_Byte(0x00);
// 5. ACK
IIC_ACK();
///6. 写入指令
IIC_Send_Byte(OLED_cmd);
//7. ACK
IIC_ACK();
//8. STOP
IIC_END();
}
//写入数据
void Oled_Write_Data(char OLED_data)
{
// 1. start()
IIC_START();
// 2. 写入从机地址 b0111 1000 0x78
IIC_Send_Byte(0x78);
// 3. ACK
IIC_ACK();
// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
IIC_Send_Byte(0x40);
// 5. ACK
IIC_ACK();
///6. 写入数据
IIC_Send_Byte(OLED_data);
//7. ACK
IIC_ACK();
//8. STOP
IIC_END();
}
//OLED初始化代码,直接复制粘贴
void Oled_Init(void){
Oled_Write_Cmd(0xAE);//--display off
Oled_Write_Cmd(0x00);//---set low column address
Oled_Write_Cmd(0x10);//---set high column address
Oled_Write_Cmd(0x40);//--set start line address
Oled_Write_Cmd(0xB0);//--set page address
Oled_Write_Cmd(0x81); // contract control
Oled_Write_Cmd(0xFF);//--128
Oled_Write_Cmd(0xA1);//set segment remap
Oled_Write_Cmd(0xA6);//--normal / reverse
Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
Oled_Write_Cmd(0x3F);//--1/32 duty
Oled_Write_Cmd(0xC8);//Com scan direction
Oled_Write_Cmd(0xD3);//-set display offset
Oled_Write_Cmd(0x00);//
Oled_Write_Cmd(0xD5);//set osc division
Oled_Write_Cmd(0x80);//
Oled_Write_Cmd(0xD8);//set area color mode off
Oled_Write_Cmd(0x05);//
Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
Oled_Write_Cmd(0xF1);//
Oled_Write_Cmd(0xDA);//set com pin configuartion
Oled_Write_Cmd(0x12);//
Oled_Write_Cmd(0xDB);//set Vcomh
Oled_Write_Cmd(0x30);//
Oled_Write_Cmd(0x8D);//set charge pump enable
Oled_Write_Cmd(0x14);//
Oled_Write_Cmd(0xAF);//--turn on oled panel
}
//清屏
void Oled_Clear()
{
unsigned char i,j; //-128 --- 127
for(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-2
unsigned int i;
Oled_Write_Cmd(0xb0+(row*2-2)); //page 0
Oled_Write_Cmd(0x00+(col&0x0f)); //low
Oled_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 1
Oled_Write_Cmd(0x00+(col&0x0f)); //low
Oled_Write_Cmd(0x10+(col>>4)); //high
for(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;
}
}
#include "reg52.h"
#include "delay.h"
sbit RightA = P1^4;
sbit RightB = P1^5;
sbit LeftA = P1^6;
sbit LeftB = P1^7;
void forward(){
LeftA = 1;
LeftB = 0;
RightA = 1;
RightB = 0;
}
void back(){
LeftA = 0;
LeftB = 1;
RightA = 0;
RightB = 1;
}
void left(){
LeftA = 0;
LeftB = 1;
RightA = 1;
RightB = 0;
}
void right(){
LeftA = 1;
LeftB = 0;
RightA = 0;
RightB = 1;
}
void stop(){
LeftA = 1;
LeftB = 1;
RightA = 1;
RightB = 1;
}
void PWM_Right(){
RightA = 1;
RightB = 0;
}
void PWM_Left(){
LeftA = 1;
LeftB = 0;
}
void PWM_STOP_Right(){
RightA = 1;
RightB = 1;
}
void PWM_STOP_Left(){
LeftA = 1;
LeftB = 1;
}