文章记录:极客领航
该项目是极客领航教程中嵌入式项目,智能小车远程控制系统
代码开源(文末)。
设计体系主要包括(如图所示)
该设计在小车上未加太多传感器,先主要实现小车的主要功能,就是遥控。对于测距、显示屏、陀螺仪、循迹等等都可以在基础功能实现后添加。在教程的后续会写循迹小车、平衡小车、玲珑机器人等文章,可参考极客领航教程体系。
下面是以前制作的小车,现在看来是有点丑,小车和遥控器可以画PCB和3D建模,这样会好看很多,感兴趣的可以看该教程的辅助设计篇。
以前设计的海报,现在看起来感觉也还行。
在这里我先不多说了,感兴趣的可以支持下,后续我也会录制作教程视频,如果觉得文章枯燥的可以看视频学。
小车主体主要模块是L298N电机驱动模块与ESP8266WiFi模块.
L298N电机驱动
核心知识点
51代码设置,我们如何定义就如何接线。
//使能端,利用PWM波实现调速 sbit ENA = P1^4; sbit ENB = P1^5;
sbit IN1 = P1^0; //定义右边控制引脚 sbit IN2 = P1^1; sbit IN3 = P1^2; //定义左边控制引脚 sbit IN4 = P1^3;
如何控制电机转动呢?
上面我们设置好引脚了,可以先让使能端接高电平,或者让使能端引脚等于高电平。
如果想让右边的电机转动,那么可以IN1 = 1,IN2 = 0,电机就会转动,如果IN1 = 0,IN2 = 1,电机就会反转。如果IN1 = 1,IN2 = 1;IN1 = 0,IN2 = 0,电机都会停止。
如何对电机进行调速呢?
电机调速,就会涉及PWM输出,在51中就会涉及定时器,占空比。
那么我们就先说下定时器的配置,主要包括初始化与中断函数。
定时器基本寄存器
中断系统图
TMOD寄存器
TCON 定时器控制寄存器
TR0 :TR0=1表示T0开始运行。(单片机中T1引脚,需要高低电平的驱动)
TR1 :TR1=1表示T1开始运行。(单片机中T0引脚,需要高低电平的驱动)
中断源优先级及中断号
在这里需要哪些内容我就列举出来,至于寄存器具体知识,可以自己去查阅,我不想文章太冗杂。
定时器初始化流程
由TMOD模式选择可知,方式1为16位定时器/计数器 16位寄存器容量为65535 定时器一旦开始,寄存器就会在原来的数值上加 1,每次加1的时间为1个机器周期 一个机器周期等于12个时钟周期,时钟周期与晶振的频率有关 如果晶振频率为12HZ,那么一个时钟周期的时间为1/12us 所以一个机器周期就为 1us 就说明,寄存器每一次计数都需要1us的时间 10000us == 10ms 如果我们将寄存器的初值设置为 65535 - 10000 = 55535 那么从55535开始计数,当达到65535时,需要计数10000次,就是10ms 我们可以控制寄存器溢出的次数,来得到准确的时间 假如我们设置每次溢出要10ms,那么100次就是 1s 又怎么设置寄存器的初值呢? 假如我们要设置初值为55535 1、对55535 / 256 = 216; 216对应的十六进制就是 1101 1000 = 0xd8 写成 TH0=0Xd8; 2、再对55535 % 256 = 239; 239对应十六进制为 1110 1111 = 0xef 写成 TL0=0Xf0;
ET0=1;//打开定时器0中断允许 EA=1;//打开总中断
根据中断系统图分享逻辑就比较清晰,后续录视频教程,文章主要是总结。
由上面分析可知,我们可以写出
定时器0的初始化函数
//定时器0初始化函数 void Timer0Config() { TMOD &= 0xF0; //清除定时器0的模式 TMOD |= 0x01; //设置定时器0模式 TH0 = 0xFF; //送入初值 (65536 - 130) / 256 65536 - 65406 TL0 = 0x7E; //(65536 - 130) % 256 EA = 1; //打开总中断 ET0 = 1; //打开定时器0中断允许 TR0 = 1; //定时器0允许控制位 }
定时器0中断函数
//定时器0 中断函数 中断号为 1 void InterruptTimer0() interrupt 1 { //每次溢出过后,都需要重新设置初值 TH0 = 0xFF; //给定时器赋初值,定时130us TL0 = 0x7E; cnt++; //每次溢出cnt自加1,130us自加一次 if(cnt >= 100) //当cnt大于等于100时,就清0 { cnt = 0; } //在这里是定义一个周期为 130 * 100 = 13000us,13ms }
定时器初始化函数和中断函数都写了,又如何控制电机呢?
目前还不行,因为我们还有写PWM函数。
我们通过一段代码来分析
IN3 = 1; if(cnt <= DutyCycle) //DutyCycle是占空比 { IN4 = 0; } else { IN4 = 1; }
因为由定时器中断函数,cnt变量在0 - 100进行循环,一个周期溢出100次;分析上面代码,当占空比DutyCycle = 60时,我们执行一百次上面的程序,cnt递增。会发现在IN3一百次都是1,IN4六十次为0,四十次为1,速度是全速的%60;当空比DutyCycle = 100时,一个周期内IN3全是1,IN4全是0,速度为全速。所以我们可以通过修改占空比来调节速度。
上面分析可能比较模糊,因为没有主观的认识,可以利用单片机和电机驱动进行测试,去修改占空比,看速度的变化,在视频教程中我也会演示。
我们将调节占空比的函数封装一下。
/* 参数定义: ReverOrCoro 传递正反转,1为正转,0为反转 DutyCycle 占空比参数 */ void Motor_Right(bit ReverOrCoro, unsigned char DutyCycle) { if(ReverOrCoro == 1) { IN3 = 1; if(cnt <= DutyCycle) { IN4 = 0; } else { IN4 = 1; } } else { IN4 = 1; if(cnt <= DutyCycle) { IN3 = 0; } else { IN3 = 1; } } }
在程序中我们如何调用的呢?
Motor_Right(0, 0); //占空比为0,停止 Motor_Right(0, 50); //占空比为50,中速 Motor_Right(0, 100); //占空比为100,全速
上面是控制右边电机的函数,左边也是一样,只是修改为IN1,IN2。
通过定时器的初始化,中断函数,PWM电机调速函数。我们已经可以控制电机的转向和转速了。如果我们要实现遥控小车,就是接收遥控器传过来的数据,进行判断。可以看下程序中的写法,下文会分析程序。
if(receiveTable[9]==0x31) //当送入字符1时,前进 { Motor_Left(1, number), Motor_Right(0, number); } if(receiveTable[9]==0x32) //当送入字符2时,后退 { Motor_Left(0, number), Motor_Right(1, number); } if(receiveTable[9]==0x33) //当送入字符3时,左转 { Motor_Left(1, number), Motor_Right(0, 0); }
到这里电机驱动的部分就完了,回顾一下,我们需要掌握哪些知识。
ESP8266
上面是两种WiFi模块,第一种是ESP8266 -01s,下面是Esp8266(NodeMCU),我们这里只是用来进行数据传输,效果相同。
ESP8266原理
ESP8266模式:
AP模式 服务器 STA模式 客户端 AP + STA模式
测试ESP8266
可以利用串口助手进行AT指令测试。
通信结构图
51和STM32原理都一样。
配置思路
连接范围
ESP8266配置成服务器(指令)
ESP8266配置成服务器(PC或单片机发指令): (1) 测试AT指令:AT (2) 更改模块波特率: AT+CIOBAUD=9600 (波特率设置成功后要更改后再进行设置其它波特率) (3) 复位重启模块:AT+RST (4) 设置为AP模式:AT+CWMODE=2 (5) 设置name password,加密方式: AT+CWSAP="esp8266","123456789",11,4 (6) 查看主机端的ip地址: AT+CIFSR(此处的IP地址是模块本身的IP,不是ST模式中加入路由器后分配的IP) (7) 设置模块传输模式为TCP模式:AT+CIPMODE=0 (8) 设置为多连接模式,启动模块:AT+CIPMUX=1 (9) 服务器的设置端口: AT+CIPSERVER=1,8090 (TCP client连接server时端口号要保持一致)
指令测试
设置AP模式、账号密码,然后就能搜索到WIFI模块了。
TCP客服端数据传输测试。
自己写的TCP客户端进行通信,成功。
通过串口助手我们可以测试相关指令,我上面测试了一遍,设置AP模式过后,再设置账号密码,然后利用TCP客户端进行连接,进行数据传输测试。
上面是在串口助手上面测试ESP8266,而我们是要把ESP8266运用到单片机上面。通过AT指令的测试,我们也发现,在串口助手上面,我们也是发送相关的指令到串口助手上面,然后通过串口发送到ESP8266上面。
如下图,我们也需要利用单片机的串口,发送指令到ESP8266上面进行配置。
所以下面我们介绍51串口的配置与使用。
串口初始化函数
//串口初始化函数 void UartInit(void) //串口初始化函数 { TMOD &= 0x0F; //清除定时器1的配置 TMOD |= 0x20; //定时器工作方式2,8位自动重载(0010 0000) TL1 = 0xfd; //装入初值 TH1 = 0xfd; //装入初值 IP = 0; //中断优先级相同 TR1 = 1; //启动定时器1 REN=1; //允许串行口接收数据 SM0=0; //工作方式1,10位异步收发 SM1=1; EA = 1; //打开全局中断控制 ES=1; //打开串行口中断 }
串口发送数据(单个字节)
//串口发送数据 单个字节 void Sent_ZF(u8 dat) //发送一个字节 { ES = 0; //不允许串行口接受、发送中断 TI=0; //循环等待发送结束,当发送结束时,TI=1了 SBUF = dat; //将dat数据存入SBUF中,为一字节 while(!TI); //当发送完成,TI = 1,那么!TI为假,结束循环 TI = 0; //TI复位 ES = 1; //允许串行口接受、发送中断 }
串口发送数据(字符串)
//串口发送数据,字符串 void AT_Send_String(u8 *string) //发送字符串 { while(*string) //当到字符尾时,'\0' 会结束循环 { Sent_ZF(*string++);//调用发送单个字节函数 Delay_ms(5); } }
串口中断函数
//串口中断函数 接收数据 void uart() interrupt 4 { if(RI == 1) //说明接收到了数据 { RI = 0; //清除串口接收标志位,只有清除了,下一次才能接收数据 receiveTable[i] = SBUF; //将数据存入数组中 if(receiveTable[0] == '+') //判断是否为无效数据,由于WiFi模块会自动加上“+PID. ”开头的字符串 //有效的数据是第十位 //假如外部通过ESP8266发送数据 5 到51上 //那么51串口接收到"+PID...5" 5是第十位 //所以receiveTable[9]才是我们真正的数据 { i++; } else { i = 0; } if(i == 10) { i = 0; } } }
ESP8266初始化函数
//ESP8266 初始化函数 void ESP8266_Init() //esp8266 9600波特率 { Delay_ms(1000); // = 2 为AP模式,= 1 为客户端模式 AT_Send_String("AT+CWMODE=2\r\n"); Delay_ms(1000); //建立WiFi热点 AT_Send_String("AT+CWSAP=esp8266,0123456789,11,4\r\n"); Delay_ms(1000); AT_Send_String("AT+RST\r\n"); //重启模块 Delay_ms(1000); //设置为多连接模式,启动模块 AT_Send_String("AT+CIPMUX=1\r\n"); Delay_ms(1000); //服务器的设置端口 AT_Send_String("AT+CIPSERVER=1,8090\r\n"); }
ESP8266配置客户端
配置服务器指令(STA模式) 1、AT+CWMODE=1 配置STA模式 2、AT+RST 重启生效 3、AT+CWJAP="wifi名称","WiFi密码" 连接WIFI 4、AT+CIPSTART="TCP","192.168.4.1",8090 连接服务器 5、AT+CIPSEND=4 发送指令 6、AT+CIPMODE=1 开启透传模式 7、AT+CIPSEND 开始透传
代码链接
链接:代码链接
提取码:1314