0.资料
项目工程文件夹
分文件原理
之前的代码
1.L9110S电机驱动模块demo
2.串口通信(习题4:PC发送字符串指令给单片机)
3.wifi模块(串口中断代码优化)
3.蓝牙模块
1.串口指令控制小车_分文件
1、和单片机的接线方式:与《L9110S电机驱动模块》一节中的连接方式一致
2、信号传递路线:
- 路线1:单片机P3^4、P3^5引脚 ——> L9110S电机驱动模块 ——> 小车左轮
- 路线2:单片机P3^2、P3^3引脚 ——> L9110S电机驱动模块 ——> 小车右轮
- 路线3:PC串口助手数据发送 ——> 单片机的RXD口 ——> 路线1/路线2 ——> 小车运动
- 路线4:单片机心跳包数据 ——> 单片机的TXD口 ——> PC串口助手显示
3、串口控制策略:
- 关键字眼确定:我们的有效指令在串口中断程序中方便随手处理,所以就不需要标志位。我用首字母'M'代表电机,关键字眼是:
电机指令 默认停止:“STOP” 前进:“M1” 后退:“M2” 左前方转:“M3” 右前方转:“M4” 左后方转:“M5” 右后方转:“M6” -
判断方法:在小车不同方向的控制中,我们的有效指令的关键字眼的第一个字符都是'M',所以我们这里使用了switch语句来进行判断。格式如下:
if(serial_buffer[0]=='M'){ switch(serial_buffer[1]){ case '1': break; case '2': break; case '3': break; case '4': break; case '5': break; case '6': break; default : stop(); break; } }
-
注意事项:在串口中断程序中需要注意memset()函数的书写位置,应该把memset()函数写在switch语句之中,而不是switch语句之后。否则会出现如下BUG:一个相同的指令需要发送两遍,单片机才会响应,只发送一遍指令是没有反应的。
- 具体为什么是这样,可以按照程序流程进行分析,上中,根据错误代码对serial_buffer字符数组的前两个元素进行了分析。
- 此外可以发现,第二次发送的指令只要第一个字符是'M',那么都可以驱动电机转动。
- 此外,其实在有效指令识别成功之后,其实是没有必要用memset()函数清零数组的,大部分情况下也不会出错。
4、主程序:【项目工程文件夹】
- 代码:
#include "reg52.h" #include "motor.h" #include "delay.h" #include "uart.h" void main(void) { UartInit(); while(1){ Delay1000ms(); sendString("hello\r\n"); //心跳包 } }
- 思路:基于《L9110S电机驱动模块》的代码,本demo主要针对串口中断程序进行了修改。
- 代码:
#include "reg52.h" #include <string.h> #include "motor.h" #define len 12 sfr AUXR = 0x8E; sbit ledD5 = P3^7; char serial_buffer[len]; void Uart_Routine() interrupt 4 { static int i = 0; //静态全局区的变量,数组下标 char temp; /* 中断处理程序中,对于接收中断的响应 */ if(RI == 1){ RI = 0;//清除接收中断标志位 temp = SBUF; if(temp=='M'){ //从数据缓冲寄存器SBUF中读到字符后,关心一件事 i = 0; } serial_buffer[i] = temp; i++; if(i == len){ i = 0; } //电机指令:默认--STOP; M1--FORWARD; M2--BACK; M3--FORWARD_LEFT; // M4--FORWARD_RIGHT; M5--BACK_LEFT; M6--BACK_RIGHT if(serial_buffer[0]=='M'){ switch(serial_buffer[1]){ case '1': goForward(); memset(serial_buffer, '\0', len); break; case '2': goBack(); memset(serial_buffer, '\0', len); break; case '3': goForward_left(); memset(serial_buffer, '\0', len); break; case '4': goForward_right(); memset(serial_buffer, '\0', len); break; case '5': goBack_left(); memset(serial_buffer, '\0', len); break; case '6': goBack_right(); memset(serial_buffer, '\0', len); break; default : stop(); break; } } } /* 中断处理程序中,对于发送中断的响应 */ if(TI == 1){ // 暂时不做任何事情 } } void UartInit(void) //[email protected] { AUXR = 0x01; SCON = 0x50; //8位UART,允许串口接收 TMOD &= 0xDF; TMOD |= 0x20; //定时器8位重载工作模式 TH1 = 0xFD; TL1 = 0xFD; //9600波特率初值 TR1 = 1; EA = 1; ES = 1; //开启串口中断 } void sendByte(char data_msg) { SBUF = data_msg; // Delay10ms(); while(TI == 0); TI = 0; } void sendString(char *str) { char *p = str; while(*p != '\0'){ sendByte(*p); p++; } }
- 代码:
#include "reg52.h" sbit RightCon_1A = P3^2; //L9110S的MotorB_1A--右轮电机下接触点 sbit RightCon_1B = P3^3; //L9110S的MotorB_1B--右轮电机上接触点 sbit LeftCon_1A = P3^4; //L9110S的MotorA_1A--左轮电机下接触点 sbit LeftCon_1B = P3^5; //L9110S的MotorA_1B--左轮电机上接触点 void goForward() { /* 右轮:前进 */ RightCon_1A = 0; RightCon_1B = 1; /* 左轮:前进 */ LeftCon_1A = 0; LeftCon_1B = 1; } void goBack() { /* 右轮:倒退 */ RightCon_1A = 1; RightCon_1B = 0; /* 左轮:倒退 */ LeftCon_1A = 1; LeftCon_1B = 0; } void goForward_left() { /* 右轮:前进 */ RightCon_1A = 0; RightCon_1B = 1; /* 左轮:不转 */ LeftCon_1A = 0; LeftCon_1B = 0; } void goForward_right() { /* 右轮:不转 */ RightCon_1A = 0; RightCon_1B = 0; /* 左轮:前进 */ LeftCon_1A = 0; LeftCon_1B = 1; } void goBack_left() { /* 右轮:倒退 */ RightCon_1A = 1; RightCon_1B = 0; /* 左轮:不转 */ LeftCon_1A = 0; LeftCon_1B = 0; } void goBack_right() { /* 右轮:不转 */ RightCon_1A = 0; RightCon_1B = 0; /* 左轮:倒退 */ LeftCon_1A = 1; LeftCon_1B = 0; } void stop() { /* 右轮:不转 */ RightCon_1A = 0; RightCon_1B = 0; /* 左轮:不转 */ LeftCon_1A = 0; LeftCon_1B = 0; }
- 代码:
#include "intrins.h" void Delay1000ms() //@11.0592MHz { unsigned char i, j, k; _nop_(); i = 8; j = 1; k = 243; do { do { while (--k); } while (--j); } while (--i); }
2.蓝牙小车的点动控制_分文件
1、和单片机的接线方式:
- 注:由于小车的电源由电池直接提供,所以在这种连接方式下,单片机开发板上的重启开关是否开启都不会影响单片机和小车模块各模块工作。
2、信号传递路线:
- 路线1:单片机P3^4、P3^5引脚 ——> L9110S电机驱动模块 ——> 小车左轮
- 路线2:单片机P3^2、P3^3引脚 ——> L9110S电机驱动模块 ——> 小车右轮
- 路线3:蓝牙APP透传数据发送 ——> HC-08蓝牙模块的RXD引脚 ——> 单片机的TXD口 ——> 路线1/路线2 ——> 小车运动
3、蓝牙APP自定义按键:我们用现成的APP,按键设置如下图所示
4、点动控制策略:
- 单片机程序方面:在串口指令控制小车的基础上,接入蓝牙模块,通过蓝牙控制小车。
- 添加点动控制,我们需要通过修改“串口”模块的代码来实现该功能。在小车没有收到指令时,就让小车停留在一个停止的状态。那么其中必定涉及到收到有效指令后电机转多久的问题,否则不可能让小车动起来,所以需要找到一个合适的延时点。这里我设置的是在串口中断程序中,小车运动延时10ms,这不会太短也不会太长。
- 原先的心跳包发送功能在这个demo中不能被使用了。因为主进程while(1)之中不能用来每隔一秒发送心跳包了,而是被停转电机的函数占用。这样我们才能保证时刻让小车保持在停止状态。
- 蓝牙APP方面:
- 如果APP支持长按时一直发数据,松开就停止发数据,就能实现前进按键长按下后小车才一直往前走的功能,但是现有APP的自定义按键不能实现,需要我们自己来制作安卓app(我们可以称之为一种上位机)。
- 虽然说我们现在还不会制作app,但是我们可以通过PC上串口助手的“自动发送数据”功能初步体验这种点动效果。
- 注意事项:
- 因为现在由电池盒来给单片机供电,但是下载程序的时候电脑也给单片机供电,所以我们下载程序的时候最好将这个电源线拔掉,避免造成单片机损坏。 烧录完成后,如果单片机插着由电池盒提供的电源,并且此时还和PC用USB线连接着,那么最好不要开启开发板上的重启开关。
- 烧录代码时,可能会出现以下警告:“UNCALLED SEGMENT”,这是因为我们在“uart.c”源文件中通过包含头文件“”对Delay1000ms();函数进行了声明,但是“uart.c”中并没有调用它。这个警告可以忽略
5、主程序“main”:【项目工程文件夹】
- 代码:相较于本节的串口指令控制小车demo修改了主进程while(1)
#include "reg52.h" #include "motor.h" #include "delay.h" #include "uart.h" void main(void) { UartInit(); while(1){ stop(); } }
- 代码:相较于本节的串口指令控制小车demo修改了串口中断程序。
#include "reg52.h" #include <string.h> #include "motor.h" #include "delay.h" #define len 12 sfr AUXR = 0x8E; sbit ledD5 = P3^7; char serial_buffer[len]; void Uart_Routine() interrupt 4 { static int i = 0; //静态全局区的变量,数组下标 char temp; /* 中断处理程序中,对于接收中断的响应 */ if(RI == 1){ RI = 0;//清除接收中断标志位 temp = SBUF; if(temp=='M'){ //从数据缓冲寄存器SBUF中读到字符后,关心一件事 i = 0; } serial_buffer[i] = temp; i++; if(i == len){ i = 0; } //电机指令:默认--STOP; M1--FORWARD; M2--BACK; M3--FORWARD_LEFT; // M4--FORWARD_RIGHT; M5--BACK_LEFT; M6--BACK_RIGHT if(serial_buffer[0]=='M'){ switch(serial_buffer[1]){ case '1': goForward(); memset(serial_buffer, '\0', len); Delay10ms(); break; case '2': goBack(); memset(serial_buffer, '\0', len); Delay10ms(); break; case '3': goForward_left(); memset(serial_buffer, '\0', len); Delay10ms(); break; case '4': goForward_right(); memset(serial_buffer, '\0', len); Delay10ms(); break; case '5': goBack_left(); memset(serial_buffer, '\0', len); Delay10ms(); break; case '6': goBack_right(); memset(serial_buffer, '\0', len); Delay10ms(); break; default : stop(); break; } } } /* 中断处理程序中,对于发送中断的响应 */ if(TI == 1){ // 暂时不做任何事情 } } void UartInit(void) //[email protected] { AUXR = 0x01; SCON = 0x50; //8位UART,允许串口接收 TMOD &= 0xDF; TMOD |= 0x20; //定时器8位重载工作模式 TH1 = 0xFD; TL1 = 0xFD; //9600波特率初值 TR1 = 1; EA = 1; ES = 1; //开启串口中断 } void sendByte(char data_msg) { SBUF = data_msg; // Delay10ms(); while(TI == 0); TI = 0; } void sendString(char *str) { char *p = str; while(*p != '\0'){ sendByte(*p); p++; } }
- 代码:相较于本节的串口指令控制小车demo没有任何改变
#include "reg52.h" sbit RightCon_1A = P3^2; //L9110S的MotorB_1A--右轮电机下接触点 sbit RightCon_1B = P3^3; //L9110S的MotorB_1B--右轮电机上接触点 sbit LeftCon_1A = P3^4; //L9110S的MotorA_1A--左轮电机下接触点 sbit LeftCon_1B = P3^5; //L9110S的MotorA_1B--左轮电机上接触点 void goForward() { /* 右轮:前进 */ RightCon_1A = 0; RightCon_1B = 1; /* 左轮:前进 */ LeftCon_1A = 0; LeftCon_1B = 1; } void goBack() { /* 右轮:倒退 */ RightCon_1A = 1; RightCon_1B = 0; /* 左轮:倒退 */ LeftCon_1A = 1; LeftCon_1B = 0; } void goForward_left() { /* 右轮:前进 */ RightCon_1A = 0; RightCon_1B = 1; /* 左轮:不转 */ LeftCon_1A = 0; LeftCon_1B = 0; } void goForward_right() { /* 右轮:不转 */ RightCon_1A = 0; RightCon_1B = 0; /* 左轮:前进 */ LeftCon_1A = 0; LeftCon_1B = 1; } void goBack_left() { /* 右轮:倒退 */ RightCon_1A = 1; RightCon_1B = 0; /* 左轮:不转 */ LeftCon_1A = 0; LeftCon_1B = 0; } void goBack_right() { /* 右轮:不转 */ RightCon_1A = 0; RightCon_1B = 0; /* 左轮:倒退 */ LeftCon_1A = 1; LeftCon_1B = 0; } void stop() { /* 右轮:不转 */ RightCon_1A = 0; RightCon_1B = 0; /* 左轮:不转 */ LeftCon_1A = 0; LeftCon_1B = 0; }
- 代码:相较于本节的串口指令控制小车demo增加了延时10ms的函数
#include "intrins.h" void Delay1000ms() //@11.0592MHz { unsigned char i, j, k; _nop_(); i = 8; j = 1; k = 243; do { do { while (--k); } while (--j); } while (--i); } void Delay10ms() //@11.0592MHz { unsigned char i, j; i = 18; j = 235; do { while (--j); } while (--i); }