文章超详细,包含全部代码
本项目基于stc89c52rc。pwm控速,语音控制切换 循迹 / 跟随 / 避障 / 蓝牙WIFI控制 等模式,包含测速屏显并传给上位机等功能。
目录
一 L9110s电机驱动模块(后改为L1298N+18650电池x2)
基于C51的智能小车
一 L9110s电机驱动模块(后改为L1298N+18650电池x2)
接通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;
}
二 小车动起来
1.控制小车前后左右停
前后左右封装函数:
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();
}
}
2.无线射频遥控
及其简单的IO操作,参考本人之前的文章,一看就会
点动和自锁模块都通用
#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温湿度监测系统项目最后的分文件操作:
1.串口
添加串口功能,用蓝牙进行控制,参考上面文章。
接收字符 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) //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 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");
}
}
2.蓝牙控制
给串口连接上蓝牙模块。

使用蓝牙串口助手,自定义设置好按键对应发送的字符,向设备发送命令。
最终实现了蓝牙控制小车方向。

3.点动模式蓝牙控制
蓝牙助手改为长按模式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();
}
}
四 PWM调速
参考本人之前的文章
原理: 全速前进是 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;
}
}
五 红外循迹、跟随
1.红外循迹模块
TCRT5000传感器的红外发射二极管不断发射红外线
当发射出的红外线没有被反射回来或被反射回来但强度不够大时,
红外接收管一直处于关断状态,此时模块的输出端为高电平,指示二极管一直处于熄灭状态
被检测物体出现在检测范围内时,红外线被反射回来且强度足够大,红外接收管饱和,
此时模块的输出端为低电平,指示二极管被点亮
总结就是一句话,没反射回来,D0输出高电平,灭灯


本文详细介绍了使用C51单片机实现智能小车的各种功能,包括PWM调速、红外循迹、超声波避障、蓝牙控制等。通过编程实现了小车的前进、后退、左右转向,并利用PWM进行速度控制。此外,还介绍了如何结合超声波模块进行避障,以及通过蓝牙接收指令进行小车的远程控制。
最低0.47元/天 解锁文章
3万+





